Merge branch 'pr-2666' into 3.7-dev
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..f151584
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,14 @@
+<!--
+Thanks for contributing! Reminders:
++ TARGET the earliest branch where you want the change
+    3.6-dev -> 3.6.8 (bugs only)
+    3.7-dev -> 3.7.3 (non-breaking)
+    master  -> 4.0.0
++ Committers will MERGE the PR forward to newer versions
++ ADD entry to the CHANGELOG.asciidoc for the targeted version
+    Do not reference a JIRA number there
++ ADD JIRA number to title and link in description
++ PRs requires 3 +1s from committers OR
+               1 +1 and 7 day wait to merge.
++ MORE details: https://s.apache.org/rtnal
+-->
\ No newline at end of file
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 1930bdf..eada449 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,7 @@
 * Allow specifying a customized Spark app name
 * CoinStep has a getter method for its probability field
 * Fixed so that TrimGlobalStep and TrimLocalStep have the same character control handling as Ltrim and Rtrim
+* Fix a bug in MaxLocalStep, MinLocalStep, MeanLocalStep and SumLocalStep that it throws NoSuchElementException when encounters an empty iterator as input.
 
 [[release-3-7-2]]
 === TinkerPop 3.7.2 (April 8, 2024)
diff --git a/docs/src/dev/developer/release.asciidoc b/docs/src/dev/developer/release.asciidoc
index 6a5e525..9812c79 100644
--- a/docs/src/dev/developer/release.asciidoc
+++ b/docs/src/dev/developer/release.asciidoc
@@ -324,6 +324,7 @@
 .. `bin/process-docs.sh` and validate the generated `SNAPSHOT` documentation locally and then `bin/publish-docs.sh <username>`
 .. Commit and push the `SNAPSHOT` changes to git
 . Examine the `future.asciidoc` and update the "Roadmap" as needed.
+. Update the version numbers in `pull_request_template.md`.
 . Send email to advise that code freeze is lifted.
 . Consider the changes made to Gremlin and determine if the community needs to organize a PR to [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/gremlin) to keep TypeScript up to date.
 . Ensure that the GLV examples compile and run with the latest image and dependencies: `bin/run-examples.sh`.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxLocalStep.java
index e4ad8b4..93b95e4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxLocalStep.java
@@ -21,11 +21,11 @@
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
-import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 import static org.apache.tinkerpop.gremlin.util.NumberHelper.max;
@@ -34,23 +34,26 @@
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Daniel Kuppitz (http://gremlin.guru)
  */
-public final class MaxLocalStep<E extends Comparable, S extends Iterable<E>> extends ScalarMapStep<S, E> {
+public final class MaxLocalStep<E extends Comparable, S extends Iterable<E>> extends MapStep<S, E> {
 
     public MaxLocalStep(final Traversal.Admin traversal) {
         super(traversal);
     }
 
     @Override
-    protected E map(final Traverser.Admin<S> traverser) {
-        final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
-        if (iterator.hasNext()) {
-            Comparable result = iterator.next();
-            while (iterator.hasNext()) {
-                result = max(iterator.next(), result);
+    protected Traverser.Admin<E> processNextStart() throws NoSuchElementException {
+        while (true) {
+            final Traverser.Admin<S> traverser = this.starts.next();
+            final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
+
+            if (iterator.hasNext()) {
+                Comparable result = iterator.next();
+                while (iterator.hasNext()) {
+                    result = max(iterator.next(), result);
+                }
+                return traverser.split((E) result, this);
             }
-            return (E) result;
         }
-        throw FastNoSuchElementException.instance();
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java
index 7a5abe7..d6e30e3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java
@@ -18,44 +18,49 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.util.NumberHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
-import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
+import static org.apache.tinkerpop.gremlin.util.NumberHelper.add;
+import static org.apache.tinkerpop.gremlin.util.NumberHelper.div;
+
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Daniel Kuppitz (http://gremlin.guru)
  */
-public final class MeanLocalStep<E extends Number, S extends Iterable<E>> extends ScalarMapStep<S, Number> {
+public final class MeanLocalStep<E extends Number, S extends Iterable<E>> extends MapStep<S, E> {
 
     public MeanLocalStep(final Traversal.Admin traversal) {
         super(traversal);
     }
 
     @Override
-    protected Number map(final Traverser.Admin<S> traverser) {
-        final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
-        if (iterator.hasNext()) {
-            // forward the iterator to the first non-null or return null
-            E result = untilNonNull(iterator);
-            Long counter = 1L;
-            while (iterator.hasNext()) {
-                final Number n = iterator.next();
-                if (n != null) {
-                    result = (E) NumberHelper.add(result, n);
-                    counter++;
+    protected Traverser.Admin<E> processNextStart() throws NoSuchElementException {
+        while (true) {
+            final Traverser.Admin<S> traverser = this.starts.next();
+            final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
+
+            if (iterator.hasNext()) {
+                // forward the iterator to the first non-null or return null
+                E result = untilNonNull(iterator);
+                Long counter = 1L;
+                while (iterator.hasNext()) {
+                    final Number n = iterator.next();
+                    if (n != null) {
+                        result = (E) add(result, n);
+                        counter++;
+                    }
                 }
+                return traverser.split((E) div(result, counter, true), this);
             }
-            return NumberHelper.div(result, counter, true);
         }
-        throw FastNoSuchElementException.instance();
     }
 
     private E untilNonNull(final Iterator<E> itty) {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinLocalStep.java
index 0781d4a..cf29a73 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinLocalStep.java
@@ -21,11 +21,11 @@
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
-import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 import static org.apache.tinkerpop.gremlin.util.NumberHelper.min;
@@ -34,23 +34,26 @@
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Daniel Kuppitz (http://gremlin.guru)
  */
-public final class MinLocalStep<E extends Comparable, S extends Iterable<E>> extends ScalarMapStep<S, E> {
+public final class MinLocalStep<E extends Comparable, S extends Iterable<E>> extends MapStep<S, E> {
 
     public MinLocalStep(final Traversal.Admin traversal) {
         super(traversal);
     }
 
     @Override
-    protected E map(final Traverser.Admin<S> traverser) {
-        final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
-        if (iterator.hasNext()) {
-            Comparable result = iterator.next();
-            while (iterator.hasNext()) {
-                result = min(iterator.next(), result);
+    protected Traverser.Admin<E> processNextStart() throws NoSuchElementException {
+        while (true) {
+            final Traverser.Admin<S> traverser = this.starts.next();
+            final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
+
+            if (iterator.hasNext()) {
+                Comparable result = iterator.next();
+                while (iterator.hasNext()) {
+                    result = min(iterator.next(), result);
+                }
+                return traverser.split((E) result, this);
             }
-            return (E) result;
         }
-        throw FastNoSuchElementException.instance();
     }
 
     @Override
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java
index 92b9796..69fd944 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java
@@ -18,39 +18,47 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 
-import org.apache.tinkerpop.gremlin.util.NumberHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
-import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
+import static org.apache.tinkerpop.gremlin.util.NumberHelper.add;
+
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  * @author Daniel Kuppitz (http://gremlin.guru)
  */
-public final class SumLocalStep<E extends Number, S extends Iterable<E>> extends ScalarMapStep<S, E> {
+public final class SumLocalStep<E extends Number, S extends Iterable<E>> extends MapStep<S, E> {
 
     public SumLocalStep(final Traversal.Admin traversal) {
         super(traversal);
     }
 
     @Override
-    protected E map(final Traverser.Admin<S> traverser) {
-        final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
-        if (iterator.hasNext()) {
-            // forward the iterator to the first non-null or return null
-            Number result = untilNonNull(iterator);
-            while (iterator.hasNext()) {
-                result = NumberHelper.add(result, iterator.next());
+    protected Traverser.Admin<E> processNextStart() throws NoSuchElementException {
+        while (true) {
+            final Traverser.Admin<S> traverser = this.starts.next();
+            final Iterator<E> iterator = IteratorUtils.asIterator(traverser.get());
+
+            if (iterator.hasNext()) {
+                // forward the iterator to the first non-null or return null
+                E result = untilNonNull(iterator);
+                while (iterator.hasNext()) {
+                    final Number n = iterator.next();
+                    if (n != null) {
+                        result = (E) add(result, n);
+                    }
+                }
+                return traverser.split(result, this);
             }
-            return (E) result;
+
         }
-        throw FastNoSuchElementException.instance();
     }
 
     private E untilNonNull(final Iterator<E> itty) {
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index eba8217..4be4287 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -835,6 +835,7 @@
                {"g_V_repeatXbothX_timesX5X_age_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both()).Times(5).Values<object>("age").Max<object>()}}, 
                {"g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_maxX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Group<object,object>().By("name").By(__.BothE().Values<object>("weight").Max<object>())}}, 
                {"g_VX1X_valuesXageX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Max<object>(Scope.Local)}}, 
+               {"g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.Union<object>(__.Values<object>("age"),__.OutE().Values<object>("weight")).Fold()).Max<object>(Scope.Local)}}, 
                {"g_V_age_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Mean<object>()}}, 
                {"g_V_foo_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Mean<object>()}}, 
                {"g_V_age_fold_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Mean<object>(Scope.Local)}}, 
@@ -851,6 +852,7 @@
                {"g_injectXnull_10_20_nullX_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Mean<object>()}}, 
                {"g_injectXlistXnull_10_20_nullXX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Mean<object>(Scope.Local)}}, 
                {"g_VX1X_valuesXageX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Mean<object>(Scope.Local)}}, 
+               {"g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.Union<object>(__.Values<object>("age"),__.OutE().Values<object>("weight")).Fold()).Mean<object>(Scope.Local)}}, 
                {"g_injectXnullX_mergeXinjectX1XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null).Merge(__.Inject(1))}}, 
                {"g_V_valuesXnameX_mergeXV_foldX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Merge(__.V().Fold())}}, 
                {"g_V_fold_mergeXconstantXnullXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Fold().Merge(__.Constant<object>(null))}}, 
@@ -996,6 +998,7 @@
                {"g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_minX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Group<object,object>().By("name").By(__.BothE().Values<object>("weight").Min<object>())}}, 
                {"g_V_foo_injectX9999999999X_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Inject(p["xx1"]).Min<object>()}}, 
                {"g_VX1X_valuesXageX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Min<object>(Scope.Local)}}, 
+               {"g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.Union<object>(__.Values<object>("age"),__.OutE().Values<object>("weight")).Fold()).Min<object>(Scope.Local)}}, 
                {"g_V_name_order", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Order()}}, 
                {"g_V_name_order_byXa1_b1X_byXb2_a2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Order().By((IComparator) p["c1"]).By((IComparator) p["c2"])}}, 
                {"g_V_order_byXname_ascX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Order().By("name",Order.Asc).Values<object>("name")}}, 
@@ -1214,6 +1217,7 @@
                {"g_injectXnull_10_5_nullX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Sum<object>()}}, 
                {"g_injectXlistXnull_10_5_nullXX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Sum<object>(Scope.Local)}}, 
                {"g_VX1X_valuesXageX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Sum<object>(Scope.Local)}}, 
+               {"g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.Union<object>(__.Values<object>("age"),__.OutE().Values<object>("weight")).Fold()).Sum<object>(Scope.Local)}}, 
                {"g_injectXfeature_test_nullX_toLower", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("FEATURE","tESt",null).ToLower()}}, 
                {"g_injectXfeature_test_nullX_toLowerXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).ToLower<object>(Scope.Local)}}, 
                {"g_injectXListXa_bXX_toLower", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).ToLower()}}, 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
index 4259e25..898f3b1 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/util/ProfilingApplication.java
@@ -30,7 +30,9 @@
 import java.io.File;
 import java.io.FileWriter;
 import java.io.PrintWriter;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Random;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
@@ -48,7 +50,7 @@
  */
 public class ProfilingApplication {
 
-    private static final Random random = new Random();
+    private static final Random random = new Random(0); // Same seed to ensure consistent test runs.
     private static final String[] scripts = new String[]{
             "g.V()",
             "g.V(1).out('knows')",
@@ -87,7 +89,7 @@
         this.exercise = exercise;
     }
 
-    public long execute() throws Exception {
+    public long executeThroughput() throws Exception {
         final AtomicInteger tooSlow = new AtomicInteger(0);
 
         final Client client = cluster.connect();
@@ -130,16 +132,51 @@
         }
     }
 
+    public double executeLatency() throws Exception {
+        final Client client = cluster.connect();
+        final String executionId = "[" + executionName + "]";
+        try {
+            client.init();
+
+            final long start = System.nanoTime();
+            int size = 0;
+            final Iterator itr = client.submitAsync(script).get().iterator();
+            try {
+                while (true) {
+                    itr.next();
+                    size++;
+                }
+            } catch (NoSuchElementException nsee) {
+                ; // Expected as hasNext() not called to increase performance.
+            }
+            final long end = System.nanoTime();
+            final long total = (end - start);
+
+
+            final double totalSeconds = total / 1000000000d;
+            System.out.println(String.format(StringUtils.rightPad(executionId, 10) + "time: %s, result count: %s", StringUtils.rightPad(String.valueOf(totalSeconds), 7), StringUtils.rightPad(String.valueOf(size), 10)));
+            return totalSeconds;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            throw new RuntimeException(ex);
+        } finally {
+            client.close();
+        }
+    }
+
     private String chooseScript() {
         return scripts[random.nextInt(scripts.length - 1)];
     }
 
+    public enum TestType { LATENCY, THROUGHPUT };
+
     public static void main(final String[] args) {
         final Map<String,Object> options = ElementHelper.asMap(args);
         final boolean noExit = Boolean.parseBoolean(options.getOrDefault("noExit", "false").toString());
         final int parallelism = Integer.parseInt(options.getOrDefault("parallelism", "16").toString());
         final BasicThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("profiler-%d").build();
         final ExecutorService executor = Executors.newFixedThreadPool(parallelism, threadFactory);
+        final TestType testType = TestType.values()[(Integer.parseInt(options.getOrDefault("testType", "1").toString()) % TestType.values().length)];
 
         final String host = options.getOrDefault("host", "localhost").toString();
         final int minExpectedRps = Integer.parseInt(options.getOrDefault("minExpectedRps", "1000").toString());
@@ -155,7 +192,7 @@
         final int maxInProcessPerConnection = Integer.parseInt(options.getOrDefault("maxInProcessPerConnection", "64").toString());
         final int minInProcessPerConnection = Integer.parseInt(options.getOrDefault("minInProcessPerConnection", "16").toString());
         final int maxWaitForConnection = Integer.parseInt(options.getOrDefault("maxWaitForConnection", "3000").toString());
-        final int workerPoolSize = Integer.parseInt(options.getOrDefault("workerPoolSize", "2").toString());
+        final int workerPoolSize = Integer.parseInt(options.getOrDefault("workerPoolSize", Runtime.getRuntime().availableProcessors() * 2).toString());
         final int tooSlowThreshold = Integer.parseInt(options.getOrDefault("tooSlowThreshold", "125").toString());
         final String channelizer = options.getOrDefault("channelizer", Channelizer.WebSocketChannelizer.class.getName()).toString();
         final String serializer = options.getOrDefault("serializer", Serializers.GRAPHBINARY_V1.name()).toString();
@@ -177,6 +214,12 @@
                 .workerPoolSize(workerPoolSize).create();
 
         try {
+            if (TestType.LATENCY == testType) {
+                System.out.println("-----------------------LATENCY TEST SELECTED----------------------");
+            } else {
+                System.out.println("---------------------THROUGHPUT TEST SELECTED---------------------");
+            }
+
             if (exercise) {
                 System.out.println("--------------------------INITIALIZATION--------------------------");
                 final Client client = cluster.connect();
@@ -189,43 +232,72 @@
                 System.out.println("Modern graph loaded");
             }
 
-            final Object fileName = options.get("store");
-            final File f = null == fileName ? null : new File(fileName.toString());
-            if (f != null && f.length() == 0) {
-                try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
-                    writer.println("parallelism\tnioPoolSize\tminConnectionPoolSize\tmaxConnectionPoolSize\tminSimultaneousUsagePerConnection\tmaxSimultaneousUsagePerConnection\tminInProcessPerConnection\tmaxInProcessPerConnection\tworkerPoolSize\trequestPerSecond");
+            if (TestType.THROUGHPUT == testType) {
+                final Object fileName = options.get("store");
+                final File f = null == fileName ? null : new File(fileName.toString());
+                if (f != null && f.length() == 0) {
+                    try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
+                        writer.println("parallelism\tnioPoolSize\tminConnectionPoolSize\tmaxConnectionPoolSize\tminSimultaneousUsagePerConnection\tmaxSimultaneousUsagePerConnection\tminInProcessPerConnection\tmaxInProcessPerConnection\tworkerPoolSize\trequestPerSecond");
+                    }
                 }
-            }
 
-            // not much point to continuing with a line of tests if we can't get at least minExpectedRps.
-            final AtomicBoolean meetsRpsExpectation = new AtomicBoolean(true);
-            System.out.println("---------------------------WARMUP CYCLE---------------------------");
-            for (int ix = 0; ix < warmups && meetsRpsExpectation.get(); ix++) {
-                final long averageRequestsPerSecond = new ProfilingApplication("warmup-" + (ix + 1), cluster, 1000, executor, script, tooSlowThreshold, exercise).execute();
-                meetsRpsExpectation.set(averageRequestsPerSecond > minExpectedRps);
-                TimeUnit.SECONDS.sleep(1); // pause between executions
-            }
-
-            final AtomicBoolean exceededTimeout = new AtomicBoolean(false);
-            long totalRequestsPerSecond = 0;
-
-            // no need to execute this if we didn't pass the basic expectation in the warmups
-            if (exercise || meetsRpsExpectation.get()) {
-                final long start = System.nanoTime();
-                System.out.println("----------------------------TEST CYCLE----------------------------");
-                for (int ix = 0; ix < executions && !exceededTimeout.get(); ix++) {
-                    totalRequestsPerSecond += new ProfilingApplication("test-" + (ix + 1), cluster, requests, executor, script, tooSlowThreshold, exercise).execute();
-                    exceededTimeout.set((System.nanoTime() - start) > TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS));
+                // not much point to continuing with a line of tests if we can't get at least minExpectedRps.
+                final AtomicBoolean meetsRpsExpectation = new AtomicBoolean(true);
+                System.out.println("---------------------------WARMUP CYCLE---------------------------");
+                for (int ix = 0; ix < warmups && meetsRpsExpectation.get(); ix++) {
+                    final long averageRequestsPerSecond = new ProfilingApplication("warmup-" + (ix + 1), cluster, 1000, executor, script, tooSlowThreshold, exercise).executeThroughput();
+                    meetsRpsExpectation.set(averageRequestsPerSecond > minExpectedRps);
                     TimeUnit.SECONDS.sleep(1); // pause between executions
                 }
-            }
 
-            final int averageRequestPerSecond = !meetsRpsExpectation.get() || exceededTimeout.get() ? 0 : Math.round(totalRequestsPerSecond / executions);
-            System.out.println(String.format("avg req/sec: %s", averageRequestPerSecond));
-            if (f != null) {
-                try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
-                    writer.println(String.join("\t", String.valueOf(parallelism), String.valueOf(nioPoolSize), String.valueOf(minConnectionPoolSize), String.valueOf(maxConnectionPoolSize), String.valueOf(minSimultaneousUsagePerConnection), String.valueOf(maxSimultaneousUsagePerConnection), String.valueOf(minInProcessPerConnection), String.valueOf(maxInProcessPerConnection), String.valueOf(workerPoolSize), String.valueOf(averageRequestPerSecond)));
+                final AtomicBoolean exceededTimeout = new AtomicBoolean(false);
+                long totalRequestsPerSecond = 0;
+
+                // no need to execute this if we didn't pass the basic expectation in the warmups
+                if (exercise || meetsRpsExpectation.get()) {
+                    final long start = System.nanoTime();
+                    System.out.println("----------------------------TEST CYCLE----------------------------");
+                    for (int ix = 0; ix < executions && !exceededTimeout.get(); ix++) {
+                        totalRequestsPerSecond += new ProfilingApplication("test-" + (ix + 1), cluster, requests, executor, script, tooSlowThreshold, exercise).executeThroughput();
+                        exceededTimeout.set((System.nanoTime() - start) > TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS));
+                        TimeUnit.SECONDS.sleep(1); // pause between executions
+                    }
                 }
+
+                final int averageRequestPerSecond = !meetsRpsExpectation.get() || exceededTimeout.get() ? 0 : Math.round(totalRequestsPerSecond / executions);
+                System.out.println(String.format("avg req/sec: %s", averageRequestPerSecond));
+                if (f != null) {
+                    try (final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(f, true)))) {
+                        writer.println(String.join("\t", String.valueOf(parallelism), String.valueOf(nioPoolSize), String.valueOf(minConnectionPoolSize), String.valueOf(maxConnectionPoolSize), String.valueOf(minSimultaneousUsagePerConnection), String.valueOf(maxSimultaneousUsagePerConnection), String.valueOf(minInProcessPerConnection), String.valueOf(maxInProcessPerConnection), String.valueOf(workerPoolSize), String.valueOf(averageRequestPerSecond)));
+                    }
+                }
+            } else if (TestType.LATENCY == testType) {
+                final AtomicBoolean meetsTimeoutExpectation = new AtomicBoolean(true);
+                System.out.println("---------------------------WARMUP CYCLE---------------------------");
+                for (int ix = 0; ix < warmups && meetsTimeoutExpectation.get(); ix++) {
+                    final double latency = new ProfilingApplication("warmup-" + (ix + 1), cluster, 1000, executor, script, tooSlowThreshold, exercise).executeLatency();
+                    meetsTimeoutExpectation.set(latency < timeout);
+                    TimeUnit.SECONDS.sleep(1); // pause between executions
+                }
+
+                final AtomicBoolean exceededTimeout = new AtomicBoolean(false);
+                double totalTime = 0;
+
+                // no need to execute this if we didn't pass the basic expectation in the warmups
+                if (exercise || meetsTimeoutExpectation.get()) {
+                    final long start = System.nanoTime();
+                    System.out.println("----------------------------TEST CYCLE----------------------------");
+                    for (int ix = 0; ix < executions && !exceededTimeout.get(); ix++) {
+                        totalTime += new ProfilingApplication("test-" + (ix + 1), cluster, requests, executor, script, tooSlowThreshold, exercise).executeLatency();
+                        exceededTimeout.set((System.nanoTime() - start) > TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS));
+                        TimeUnit.SECONDS.sleep(1); // pause between executions
+                    }
+                }
+
+                final double averageLatency = !meetsTimeoutExpectation.get() || exceededTimeout.get() ? 0 : (totalTime / executions);
+                System.out.println(String.format("avg latency (sec/req): %s", averageLatency));
+            } else {
+                System.out.println("Encountered unknown testType. Please enter a valid value and try again.");
             }
 
             if (!noExit) System.exit(0);
diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go
index 0922e72..0bd7ba6 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -806,6 +806,7 @@
     "g_V_repeatXbothX_timesX5X_age_max": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Repeat(gremlingo.T__.Both()).Times(5).Values("age").Max()}}, 
     "g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_maxX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Group().By("name").By(gremlingo.T__.BothE().Values("weight").Max())}}, 
     "g_VX1X_valuesXageX_maxXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Max(gremlingo.Scope.Local)}}, 
+    "g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_maxXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.Union(gremlingo.T__.Values("age"), gremlingo.T__.OutE().Values("weight")).Fold()).Max(gremlingo.Scope.Local)}}, 
     "g_V_age_mean": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Mean()}}, 
     "g_V_foo_mean": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("foo").Mean()}}, 
     "g_V_age_fold_meanXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("age").Fold().Mean(gremlingo.Scope.Local)}}, 
@@ -822,6 +823,7 @@
     "g_injectXnull_10_20_nullX_mean": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, p["xx1"], p["xx2"], nil).Mean()}}, 
     "g_injectXlistXnull_10_20_nullXX_meanXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Mean(gremlingo.Scope.Local)}}, 
     "g_VX1X_valuesXageX_meanXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Mean(gremlingo.Scope.Local)}}, 
+    "g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_meanXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.Union(gremlingo.T__.Values("age"), gremlingo.T__.OutE().Values("weight")).Fold()).Mean(gremlingo.Scope.Local)}}, 
     "g_injectXnullX_mergeXinjectX1XX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil).Merge(gremlingo.T__.Inject(1))}}, 
     "g_V_valuesXnameX_mergeXV_foldX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Merge(gremlingo.T__.V().Fold())}}, 
     "g_V_fold_mergeXconstantXnullXX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Fold().Merge(gremlingo.T__.Constant(nil))}}, 
@@ -967,6 +969,7 @@
     "g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_minX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().HasLabel("software").Group().By("name").By(gremlingo.T__.BothE().Values("weight").Min())}}, 
     "g_V_foo_injectX9999999999X_min": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("foo").Inject(p["xx1"]).Min()}}, 
     "g_VX1X_valuesXageX_minXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Min(gremlingo.Scope.Local)}}, 
+    "g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_minXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.Union(gremlingo.T__.Values("age"), gremlingo.T__.OutE().Values("weight")).Fold()).Min(gremlingo.Scope.Local)}}, 
     "g_V_name_order": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Order()}}, 
     "g_V_name_order_byXa1_b1X_byXb2_a2X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Values("name").Order().By(p["c1"]).By(p["c2"])}}, 
     "g_V_order_byXname_ascX_name": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Order().By("name", gremlingo.Order.Asc).Values("name")}}, 
@@ -1185,6 +1188,7 @@
     "g_injectXnull_10_5_nullX_sum": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, p["xx1"], p["xx2"], nil).Sum()}}, 
     "g_injectXlistXnull_10_5_nullXX_sumXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).Sum(gremlingo.Scope.Local)}}, 
     "g_VX1X_valuesXageX_sumXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["vid1"]).Values("age").Sum(gremlingo.Scope.Local)}}, 
+    "g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_sumXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V().Local(gremlingo.T__.Union(gremlingo.T__.Values("age"), gremlingo.T__.OutE().Values("weight")).Fold()).Sum(gremlingo.Scope.Local)}}, 
     "g_injectXfeature_test_nullX_toLower": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject("FEATURE", "tESt", nil).ToLower()}}, 
     "g_injectXfeature_test_nullX_toLowerXlocalX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).ToLower(gremlingo.Scope.Local)}}, 
     "g_injectXListXa_bXX_toLower": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"]).ToLower()}}, 
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 8eaca3b..c666394 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -826,6 +826,7 @@
     g_V_repeatXbothX_timesX5X_age_max: [function({g}) { return g.V().repeat(__.both()).times(5).values("age").max() }], 
     g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_maxX: [function({g}) { return g.V().hasLabel("software").group().by("name").by(__.bothE().values("weight").max()) }], 
     g_VX1X_valuesXageX_maxXlocalX: [function({g, vid1}) { return g.V(vid1).values("age").max(Scope.local) }], 
+    g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_maxXlocalX: [function({g}) { return g.V().local(__.union(__.values("age"),__.outE().values("weight")).fold()).max(Scope.local) }], 
     g_V_age_mean: [function({g}) { return g.V().values("age").mean() }], 
     g_V_foo_mean: [function({g}) { return g.V().values("foo").mean() }], 
     g_V_age_fold_meanXlocalX: [function({g}) { return g.V().values("age").fold().mean(Scope.local) }], 
@@ -842,6 +843,7 @@
     g_injectXnull_10_20_nullX_mean: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).mean() }], 
     g_injectXlistXnull_10_20_nullXX_meanXlocalX: [function({g, xx1}) { return g.inject(xx1).mean(Scope.local) }], 
     g_VX1X_valuesXageX_meanXlocalX: [function({g, vid1}) { return g.V(vid1).values("age").mean(Scope.local) }], 
+    g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_meanXlocalX: [function({g}) { return g.V().local(__.union(__.values("age"),__.outE().values("weight")).fold()).mean(Scope.local) }], 
     g_injectXnullX_mergeXinjectX1XX: [function({g}) { return g.inject(null).merge(__.inject(1)) }], 
     g_V_valuesXnameX_mergeXV_foldX: [function({g}) { return g.V().values("name").merge(__.V().fold()) }], 
     g_V_fold_mergeXconstantXnullXX: [function({g}) { return g.V().fold().merge(__.constant(null)) }], 
@@ -987,6 +989,7 @@
     g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_minX: [function({g}) { return g.V().hasLabel("software").group().by("name").by(__.bothE().values("weight").min()) }], 
     g_V_foo_injectX9999999999X_min: [function({g, xx1}) { return g.V().values("foo").inject(xx1).min() }], 
     g_VX1X_valuesXageX_minXlocalX: [function({g, vid1}) { return g.V(vid1).values("age").min(Scope.local) }], 
+    g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_minXlocalX: [function({g}) { return g.V().local(__.union(__.values("age"),__.outE().values("weight")).fold()).min(Scope.local) }], 
     g_V_name_order: [function({g}) { return g.V().values("name").order() }], 
     g_V_name_order_byXa1_b1X_byXb2_a2X: [function({g, c1, c2}) { return g.V().values("name").order().by(c1).by(c2) }], 
     g_V_order_byXname_ascX_name: [function({g}) { return g.V().order().by("name",Order.asc).values("name") }], 
@@ -1205,6 +1208,7 @@
     g_injectXnull_10_5_nullX_sum: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).sum() }], 
     g_injectXlistXnull_10_5_nullXX_sumXlocalX: [function({g, xx1}) { return g.inject(xx1).sum(Scope.local) }], 
     g_VX1X_valuesXageX_sumXlocalX: [function({g, vid1}) { return g.V(vid1).values("age").sum(Scope.local) }], 
+    g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_sumXlocalX: [function({g}) { return g.V().local(__.union(__.values("age"),__.outE().values("weight")).fold()).sum(Scope.local) }], 
     g_injectXfeature_test_nullX_toLower: [function({g}) { return g.inject("FEATURE","tESt",null).toLower() }], 
     g_injectXfeature_test_nullX_toLowerXlocalX: [function({g, xx1}) { return g.inject(xx1).toLower(Scope.local) }], 
     g_injectXListXa_bXX_toLower: [function({g, xx1}) { return g.inject(xx1).toLower() }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 9847e87..8696202 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -808,6 +808,7 @@
     'g_V_repeatXbothX_timesX5X_age_max': [(lambda g:g.V().repeat(__.both()).times(5).age.max_())], 
     'g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_maxX': [(lambda g:g.V().hasLabel('software').group().by('name').by(__.bothE().weight.max_()))], 
     'g_VX1X_valuesXageX_maxXlocalX': [(lambda g, vid1=None:g.V(vid1).age.max_(Scope.local))], 
+    'g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_maxXlocalX': [(lambda g:g.V().local(__.union(__.age,__.outE().weight).fold()).max_(Scope.local))], 
     'g_V_age_mean': [(lambda g:g.V().age.mean())], 
     'g_V_foo_mean': [(lambda g:g.V().foo.mean())], 
     'g_V_age_fold_meanXlocalX': [(lambda g:g.V().age.fold().mean(Scope.local))], 
@@ -824,6 +825,7 @@
     'g_injectXnull_10_20_nullX_mean': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).mean())], 
     'g_injectXlistXnull_10_20_nullXX_meanXlocalX': [(lambda g, xx1=None:g.inject(xx1).mean(Scope.local))], 
     'g_VX1X_valuesXageX_meanXlocalX': [(lambda g, vid1=None:g.V(vid1).age.mean(Scope.local))], 
+    'g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_meanXlocalX': [(lambda g:g.V().local(__.union(__.age,__.outE().weight).fold()).mean(Scope.local))], 
     'g_injectXnullX_mergeXinjectX1XX': [(lambda g:g.inject(None).merge(__.inject(1)))], 
     'g_V_valuesXnameX_mergeXV_foldX': [(lambda g:g.V().name.merge(__.V().fold()))], 
     'g_V_fold_mergeXconstantXnullXX': [(lambda g:g.V().fold().merge(__.constant(None)))], 
@@ -969,6 +971,7 @@
     'g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_minX': [(lambda g:g.V().hasLabel('software').group().by('name').by(__.bothE().weight.min_()))], 
     'g_V_foo_injectX9999999999X_min': [(lambda g, xx1=None:g.V().foo.inject(xx1).min_())], 
     'g_VX1X_valuesXageX_minXlocalX': [(lambda g, vid1=None:g.V(vid1).age.min_(Scope.local))], 
+    'g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_minXlocalX': [(lambda g:g.V().local(__.union(__.age,__.outE().weight).fold()).min_(Scope.local))], 
     'g_V_name_order': [(lambda g:g.V().name.order())], 
     'g_V_name_order_byXa1_b1X_byXb2_a2X': [(lambda g, c1=None,c2=None:g.V().name.order().by(c1).by(c2))], 
     'g_V_order_byXname_ascX_name': [(lambda g:g.V().order().by('name',Order.asc).name)], 
@@ -1187,6 +1190,7 @@
     'g_injectXnull_10_5_nullX_sum': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).sum_())], 
     'g_injectXlistXnull_10_5_nullXX_sumXlocalX': [(lambda g, xx1=None:g.inject(xx1).sum_(Scope.local))], 
     'g_VX1X_valuesXageX_sumXlocalX': [(lambda g, vid1=None:g.V(vid1).age.sum_(Scope.local))], 
+    'g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_sumXlocalX': [(lambda g:g.V().local(__.union(__.age,__.outE().weight).fold()).sum_(Scope.local))], 
     'g_injectXfeature_test_nullX_toLower': [(lambda g:g.inject('FEATURE','tESt',None).to_lower())], 
     'g_injectXfeature_test_nullX_toLowerXlocalX': [(lambda g, xx1=None:g.inject(xx1).to_lower(Scope.local))], 
     'g_injectXListXa_bXX_toLower': [(lambda g, xx1=None:g.inject(xx1).to_lower())], 
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Max.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Max.feature
index 1603370..75caa6a 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Max.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Max.feature
@@ -201,3 +201,18 @@
     Then the result should be unordered
       | result |
       | d[29].i |
+
+  # It verifies if empty lists are filtered out as expected
+  Scenario: g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_maxXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().local(union(values("age"), outE().values("weight")).fold()).max(local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[29].d |
+      | d[27].i |
+      | d[32].d |
+      | d[35].d |
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Mean.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Mean.feature
index 75fc132..fed1a0c 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Mean.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Mean.feature
@@ -202,4 +202,19 @@
     When iterated to list
     Then the result should be unordered
       | result |
-      | d[29.0].d |
\ No newline at end of file
+      | d[29.0].d |
+
+  # It verifies if empty lists are filtered out as expected
+  Scenario: g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_meanXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().local(union(values("age"), outE().values("weight")).fold()).mean(local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[7.725].d |
+      | d[27.0].d |
+      | d[11.133333333333333].d |
+      | d[17.6].d |
\ No newline at end of file
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Min.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Min.feature
index 0b8c4af..cff82d8 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Min.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Min.feature
@@ -214,3 +214,18 @@
     Then the result should be unordered
       | result |
       | d[29].i |
+
+  # It verifies if empty lists are filtered out as expected
+  Scenario: g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_minXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().local(union(values("age"), outE().values("weight")).fold()).min(local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[0.4].d |
+      | d[27].i |
+      | d[0.4].d |
+      | d[0.2].d |
\ No newline at end of file
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Sum.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Sum.feature
index e0e7345..c5a475f 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Sum.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/map/Sum.feature
@@ -201,4 +201,19 @@
     When iterated to list
     Then the result should be unordered
       | result |
-      | d[29].i |
\ No newline at end of file
+      | d[29].i |
+
+      # It verifies if empty lists are filtered out as expected
+  Scenario: g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_sumXlocalX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().local(union(values("age"), outE().values("weight")).fold()).sum(local)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | d[30.9].d |
+      | d[27].i |
+      | d[33.4].d |
+      | d[35.2].d |
\ No newline at end of file