Compaction of discrete resources with range based representation

This resolves ONOS-4281

Change-Id: I0739ba94cc0b3ce617e2db44307fef396dcfb942
(cherry picked from commit 34e2c1c223ec62c85c2613c0d899a0cc06b63f33)
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java
index ec121cd..1b4ded4 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java
@@ -15,12 +15,18 @@
  */
 package org.onosproject.store.resource.impl;
 
+import com.google.common.collect.Sets;
 import org.onosproject.net.resource.DiscreteResource;
 import org.onosproject.net.resource.DiscreteResourceId;
+import org.onosproject.net.resource.Resources;
 
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Represents a set of resources containing resources that can be encoded as integer
@@ -28,55 +34,66 @@
  */
 final class UnifiedDiscreteResources implements DiscreteResources {
     private final DiscreteResources nonEncodables;
+    private final DiscreteResources encodables;
+    private static final Codecs CODECS = Codecs.getInstance();
 
-    static DiscreteResources empty() {
-        return new UnifiedDiscreteResources();
+    static DiscreteResources of(Set<DiscreteResource> resources) {
+        if (resources.isEmpty()) {
+            return DiscreteResources.empty();
+        }
+
+        Map<Boolean, Set<DiscreteResource>> partitioned = resources.stream()
+                .collect(Collectors.partitioningBy(CODECS::isEncodable, Collectors.toCollection(LinkedHashSet::new)));
+        return new UnifiedDiscreteResources(
+                NonEncodableDiscreteResources.of(partitioned.get(false)),
+                EncodableDiscreteResources.of(partitioned.get(true))
+        );
     }
 
-    static DiscreteResources of(List<DiscreteResource> resources) {
-        return new UnifiedDiscreteResources(resources);
-    }
-
-    private UnifiedDiscreteResources() {
-        this.nonEncodables = NonEncodableDiscreteResources.empty();
-    }
-
-    private UnifiedDiscreteResources(List<DiscreteResource> resources) {
-        this.nonEncodables = NonEncodableDiscreteResources.of(resources);
+    private UnifiedDiscreteResources(DiscreteResources nonEncodables, DiscreteResources encodables) {
+        this.nonEncodables = nonEncodables;
+        this.encodables = encodables;
     }
 
     @Override
     public Optional<DiscreteResource> lookup(DiscreteResourceId id) {
+        if (CODECS.isEncodable(Resources.discrete(id).resource())) {
+            return encodables.lookup(id);
+        }
+
         return nonEncodables.lookup(id);
     }
 
     @Override
     public DiscreteResources difference(DiscreteResources other) {
-        return nonEncodables.difference(other);
+        return of(Sets.difference(values(), other.values()));
     }
 
     @Override
     public boolean isEmpty() {
-        return nonEncodables.isEmpty();
+        return nonEncodables.isEmpty() && encodables.isEmpty();
     }
 
     @Override
     public boolean containsAny(List<DiscreteResource> other) {
-        return nonEncodables.containsAny(other);
+        Map<Boolean, List<DiscreteResource>> partitioned = other.stream()
+                .collect(Collectors.partitioningBy(CODECS::isEncodable));
+        return nonEncodables.containsAny(partitioned.get(false)) || encodables.containsAny(partitioned.get(true));
     }
 
     @Override
     public DiscreteResources add(DiscreteResources other) {
-        return nonEncodables.add(other);
+        return of(Sets.union(this.values(), other.values()));
     }
 
     @Override
     public DiscreteResources remove(List<DiscreteResource> removed) {
-        return nonEncodables.remove(removed);
+        return of(Sets.difference(values(), new LinkedHashSet<>(removed)));
     }
 
     @Override
     public Set<DiscreteResource> values() {
-        return nonEncodables.values();
+        return Stream.concat(encodables.values().stream(), nonEncodables.values().stream())
+                .collect(Collectors.toCollection(LinkedHashSet::new));
     }
 }