From b6e689314b2ec9c2d62e4d532c125d2fd879df8b Mon Sep 17 00:00:00 2001
From: TeamDman <TeamDman9201@gmail.com>
Date: Mon, 7 Oct 2024 00:34:14 -0400
Subject: [PATCH] performance optimization

---
 .../common/program/IInputResourceTracker.java | 10 ++++---
 .../program/IOutputResourceTracker.java       | 10 ++++---
 ...mulateExploreAllPathsProgramBehaviour.java |  6 ++--
 .../ca/teamdman/sfml/ast/InputStatement.java  | 30 +++++++++----------
 .../ca/teamdman/sfml/ast/OutputStatement.java | 23 +++++++-------
 .../teamdman/sfml/ast/ResourceComparer.java   |  2 +-
 .../ca/teamdman/sfml/ast/ResourceIdSet.java   | 18 +++++------
 .../teamdman/sfml/ast/ResourceIdentifier.java |  5 ++--
 .../ca/teamdman/sfml/ast/ResourceLimits.java  | 15 +++++-----
 9 files changed, 57 insertions(+), 62 deletions(-)

diff --git a/src/main/java/ca/teamdman/sfm/common/program/IInputResourceTracker.java b/src/main/java/ca/teamdman/sfm/common/program/IInputResourceTracker.java
index b863e83cc..7a52880b0 100644
--- a/src/main/java/ca/teamdman/sfm/common/program/IInputResourceTracker.java
+++ b/src/main/java/ca/teamdman/sfm/common/program/IInputResourceTracker.java
@@ -55,9 +55,11 @@ default boolean matchesStack(Object stack) {
     }
 
     default boolean matchesCapabilityType(Object capability) {
-        return getResourceLimit()
-                .resourceIds()
-                .getReferencedResourceTypes()
-                .anyMatch(rt -> rt.matchesCapabilityType(capability));
+        for (ResourceType<?, ?, ?> resourceType : getResourceLimit().resourceIds().getReferencedResourceTypes()) {
+            if (resourceType.matchesCapabilityType(capability)) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/src/main/java/ca/teamdman/sfm/common/program/IOutputResourceTracker.java b/src/main/java/ca/teamdman/sfm/common/program/IOutputResourceTracker.java
index fbc077025..5999cc67a 100644
--- a/src/main/java/ca/teamdman/sfm/common/program/IOutputResourceTracker.java
+++ b/src/main/java/ca/teamdman/sfm/common/program/IOutputResourceTracker.java
@@ -31,10 +31,12 @@ <STACK, ITEM, CAP> long getMaxTransferable(
     );
 
     default boolean matchesCapabilityType(Object capability) {
-        return getResourceLimit()
-                .resourceIds()
-                .getReferencedResourceTypes()
-                .anyMatch(rt -> rt.matchesCapabilityType(capability));
+        for (ResourceType<?, ?, ?> resourceType : getResourceLimit().resourceIds().getReferencedResourceTypes()) {
+            if (resourceType.matchesCapabilityType(capability)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     default boolean matchesStack(Object stack) {
diff --git a/src/main/java/ca/teamdman/sfm/common/program/SimulateExploreAllPathsProgramBehaviour.java b/src/main/java/ca/teamdman/sfm/common/program/SimulateExploreAllPathsProgramBehaviour.java
index 25daf03d6..7cac99ac7 100644
--- a/src/main/java/ca/teamdman/sfm/common/program/SimulateExploreAllPathsProgramBehaviour.java
+++ b/src/main/java/ca/teamdman/sfm/common/program/SimulateExploreAllPathsProgramBehaviour.java
@@ -11,7 +11,6 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 public class SimulateExploreAllPathsProgramBehaviour implements ProgramBehaviour {
@@ -182,11 +181,10 @@ public record Branch(
     ) implements ExecutionPathElement {
     }
 
-    @SuppressWarnings("rawtypes")
     public record IO(
             IOStatement statement,
             IOKind kind,
-            Set<ResourceType> usedResourceTypes,
+            Set<ResourceType<?,?,?>> usedResourceTypes,
             Set<Label> usedLabels
     ) implements ExecutionPathElement {
         public IO(IOStatement statement) {
@@ -196,7 +194,7 @@ public IO(IOStatement statement) {
                     statement instanceof InputStatement
                     ? IOKind.INPUT
                     : (statement instanceof OutputStatement ? IOKind.OUTPUT : null),
-                    statement.resourceLimits().getReferencedResourceTypes().collect(Collectors.toSet()),
+                    statement.resourceLimits().getReferencedResourceTypes(),
                     new HashSet<>(statement.labelAccess().labels())
             );
             if (kind == null) {
diff --git a/src/main/java/ca/teamdman/sfml/ast/InputStatement.java b/src/main/java/ca/teamdman/sfml/ast/InputStatement.java
index 87720e0dd..e230ee8b0 100644
--- a/src/main/java/ca/teamdman/sfml/ast/InputStatement.java
+++ b/src/main/java/ca/teamdman/sfml/ast/InputStatement.java
@@ -7,7 +7,10 @@
 import net.minecraft.core.Direction;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.*;
+import java.util.ArrayDeque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -75,31 +78,26 @@ public void gatherSlots(
             };
         }
 
-        // identify distinct resource types for capability gathering
-        Set<ResourceType> referencedResourceTypes = resourceLimits
-                .getReferencedResourceTypes()
-                .collect(Collectors.toSet());
-
         if (!each) {
             // log not each
             context.getLogger().debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_NOT_EACH.get()));
 
             // create a single matcher to be shared by all capabilities
             List<IInputResourceTracker> inputTrackers = resourceLimits.createInputTrackers();
-            for (var type : referencedResourceTypes) {
+            for (var resourceType : resourceLimits.getReferencedResourceTypes()) {
                 // log gather for resource type
                 context
                         .getLogger()
                         .debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_FOR_RESOURCE_TYPE.get(
-                                type.displayAsCapabilityClass(),
-                                type.displayAsCapabilityClass()
+                                resourceType.displayAsCapabilityClass(),
+                                resourceType.displayAsCapabilityClass()
                         )));
 
                 // gather slots for each capability found for positions tagged by a provided label
                 Consumer<LimitedInputSlot<?, ?, ?>> finalSlotConsumer = slotConsumer;
-                type.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> gatherSlotsForCap(
+                resourceType.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> gatherSlotsForCap(
                         context,
-                        (ResourceType<Object, Object, Object>) type,
+                        (ResourceType<Object, Object, Object>) resourceType,
                         label, pos, direction, cap,
                         inputTrackers,
                         finalSlotConsumer
@@ -109,22 +107,22 @@ public void gatherSlots(
             // log yes each
             context.getLogger().debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_EACH.get()));
 
-            for (ResourceType type : referencedResourceTypes) {
+            for (var resourceType : resourceLimits.getReferencedResourceTypes()) {
                 // log gather for resource type
                 context
                         .getLogger()
                         .debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_FOR_RESOURCE_TYPE.get(
-                                type.displayAsCapabilityClass(),
-                                type.displayAsCapabilityClass()
+                                resourceType.displayAsCapabilityClass(),
+                                resourceType.displayAsCapabilityClass()
                         )));
 
                 // gather slots for each capability found for positions tagged by a provided label
                 Consumer<LimitedInputSlot<?, ?, ?>> finalSlotConsumer = slotConsumer;
-                type.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> {
+                resourceType.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> {
                     List<IInputResourceTracker> inputTrackers = resourceLimits.createInputTrackers();
                     gatherSlotsForCap(
                             context,
-                            (ResourceType<Object, Object, Object>) type,
+                            (ResourceType<Object, Object, Object>) resourceType,
                             label, pos, direction, cap,
                             inputTrackers,
                             finalSlotConsumer
diff --git a/src/main/java/ca/teamdman/sfml/ast/OutputStatement.java b/src/main/java/ca/teamdman/sfml/ast/OutputStatement.java
index 9d92c86ae..43f549543 100644
--- a/src/main/java/ca/teamdman/sfml/ast/OutputStatement.java
+++ b/src/main/java/ca/teamdman/sfml/ast/OutputStatement.java
@@ -13,7 +13,6 @@
 import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import static ca.teamdman.sfm.common.localization.LocalizationKeys.*;
 
@@ -346,23 +345,21 @@ public void gatherSlots(
     ) {
         context.getLogger().debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS.get(toStringPretty())));
 
-        Stream<ResourceType> types = resourceLimits.getReferencedResourceTypes();
-
         if (!each) {
             context.getLogger().debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_NOT_EACH.get()));
             // create a single list of trackers to be shared between all limited slots
             List<IOutputResourceTracker> outputTracker = resourceLimits.createOutputTrackers();
-            for (var type : (Iterable<ResourceType>) types::iterator) {
+            for (var resourceType : resourceLimits.getReferencedResourceTypes()) {
                 context
                         .getLogger()
                         .debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_FOR_RESOURCE_TYPE.get(
-                                type.displayAsCapabilityClass(),
-                                type.displayAsCapabilityClass()
+                                resourceType.displayAsCapabilityClass(),
+                                resourceType.displayAsCapabilityClass()
                         )));
-                type.forEachCapability(context, labelAccess, (
+                resourceType.forEachCapability(context, labelAccess, (
                         (label, pos, direction, cap) -> gatherSlotsForCap(
                                 context,
-                                (ResourceType<Object, Object, Object>) type,
+                                (ResourceType<Object, Object, Object>) resourceType,
                                 label,
                                 pos,
                                 direction,
@@ -374,19 +371,19 @@ public void gatherSlots(
             }
         } else {
             context.getLogger().debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_EACH.get()));
-            for (var type : (Iterable<ResourceType>) types::iterator) {
+            for (var resourceType : resourceLimits.getReferencedResourceTypes()) {
                 context
                         .getLogger()
                         .debug(x -> x.accept(LOG_PROGRAM_TICK_IO_STATEMENT_GATHER_SLOTS_FOR_RESOURCE_TYPE.get(
-                                type.displayAsCapabilityClass(),
-                                type.displayAsCapabilityClass()
+                                resourceType.displayAsCapabilityClass(),
+                                resourceType.displayAsCapabilityClass()
                         )));
-                type.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> {
+                resourceType.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> {
                     // create a new list of trackers for each limited slot
                     List<IOutputResourceTracker> outputTracker = resourceLimits.createOutputTrackers();
                     gatherSlotsForCap(
                             context,
-                            (ResourceType<Object, Object, Object>) type,
+                            (ResourceType<Object, Object, Object>) resourceType,
                             label,
                             pos,
                             direction,
diff --git a/src/main/java/ca/teamdman/sfml/ast/ResourceComparer.java b/src/main/java/ca/teamdman/sfml/ast/ResourceComparer.java
index 118ad1e3c..69c20b520 100644
--- a/src/main/java/ca/teamdman/sfml/ast/ResourceComparer.java
+++ b/src/main/java/ca/teamdman/sfml/ast/ResourceComparer.java
@@ -24,7 +24,7 @@ public BoolExpr toBooleanExpression(SetOperator setOp, LabelAccess labelAccess,
                     type.forEachCapability(context, labelAccess, (label, pos, direction, cap) -> {
                         long inThisInv = 0;
                         for (var stack : (Iterable<STACK>) type.getStacksInSlots(cap, labelAccess.slots())::iterator) {
-                            if (this.res.test(stack)) {
+                            if (this.res.matchesStack(stack)) {
                                 inThisInv += type.getAmount(stack);
                                 overallCount.addAndGet(type.getAmount(stack));
                             }
diff --git a/src/main/java/ca/teamdman/sfml/ast/ResourceIdSet.java b/src/main/java/ca/teamdman/sfml/ast/ResourceIdSet.java
index eb9e4b7db..c62a281c1 100644
--- a/src/main/java/ca/teamdman/sfml/ast/ResourceIdSet.java
+++ b/src/main/java/ca/teamdman/sfml/ast/ResourceIdSet.java
@@ -4,10 +4,7 @@
 import net.minecraft.resources.ResourceLocation;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -24,11 +21,12 @@ public ResourceIdSet(Collection<ResourceIdentifier<?, ?, ?>> contents) {
         this(new LinkedHashSet<>(contents));
     }
 
-    @SuppressWarnings("rawtypes")
-    public Stream<ResourceType> getReferencedResourceTypes() { // todo: cache maybe? check perf
-        return this.stream()
-                .map((ResourceIdentifier x) -> x.getResourceType())
-                .distinct();
+    public Set<ResourceType<?,?,?>> getReferencedResourceTypes() {
+        HashSet<ResourceType<?,?,?>> rtn = new HashSet<>(8);
+        for (ResourceIdentifier<?, ?, ?> resourceId : this.resourceIds) {
+            rtn.add(resourceId.getResourceType());
+        }
+        return rtn;
     }
 
     public boolean couldMatchMoreThanOne() {
@@ -51,7 +49,7 @@ public ResourceIdSet(
 
     public @Nullable ResourceIdentifier<?, ?, ?> getMatchingFromStack(Object stack) {
         for (ResourceIdentifier<?, ?, ?> entry : resourceIds) {
-            if (entry.test(stack)) {
+            if (entry.matchesStack(stack)) {
                 return entry;
             }
         }
diff --git a/src/main/java/ca/teamdman/sfml/ast/ResourceIdentifier.java b/src/main/java/ca/teamdman/sfml/ast/ResourceIdentifier.java
index 1ba5e06a3..84da51d2a 100644
--- a/src/main/java/ca/teamdman/sfml/ast/ResourceIdentifier.java
+++ b/src/main/java/ca/teamdman/sfml/ast/ResourceIdentifier.java
@@ -17,7 +17,7 @@
 
 // resourceTypeName resourceNamespace, resourceTypeName name, resource resourceNamespace, resource name
 // sfm:item:minecraft:stone
-public class ResourceIdentifier<STACK, ITEM, CAP> implements ASTNode, Predicate<Object> {
+public class ResourceIdentifier<STACK, ITEM, CAP> implements ASTNode {
 
     public static final ResourceIdentifier<?, ?, ?> MATCH_ALL = new ResourceIdentifier<>(
             ".*",
@@ -111,8 +111,7 @@ public Optional<ResourceLocation> getLocation() {
         }
     }
 
-    @Override
-    public boolean test(Object other) {
+    public boolean matchesStack(Object other) {
         ResourceType<STACK, ITEM, CAP> resourceType = getResourceType();
         return resourceType != null && resourceType.matchesStack(this, other);
     }
diff --git a/src/main/java/ca/teamdman/sfml/ast/ResourceLimits.java b/src/main/java/ca/teamdman/sfml/ast/ResourceLimits.java
index 14b89b5c5..1ede4b192 100644
--- a/src/main/java/ca/teamdman/sfml/ast/ResourceLimits.java
+++ b/src/main/java/ca/teamdman/sfml/ast/ResourceLimits.java
@@ -5,9 +5,10 @@
 import ca.teamdman.sfm.common.resourcetype.ResourceType;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 public record ResourceLimits(
         List<ResourceLimit> resourceLimitList,
@@ -36,12 +37,12 @@ public ResourceLimits withExclusions(ResourceIdSet exclusions) {
         return new ResourceLimits(resourceLimitList, exclusions);
     }
 
-    @SuppressWarnings("rawtypes")
-    public Stream<ResourceType> getReferencedResourceTypes() {
-        return resourceLimitList()
-                .stream()
-                .flatMap(resourceLimits -> resourceLimits.resourceIds().getReferencedResourceTypes())
-                .distinct();
+    public Set<ResourceType<?,?,?>> getReferencedResourceTypes() {
+        Set<ResourceType<?,?,?>> rtn = new HashSet<>(8);
+        for (ResourceLimit resourceLimit : resourceLimitList) {
+            rtn.addAll(resourceLimit.resourceIds().getReferencedResourceTypes());
+        }
+        return rtn;
     }
 
     @Override