From 54e31697a8dd477d4d29d1430d5fb9a7cc6ad4aa Mon Sep 17 00:00:00 2001 From: iMoHax Date: Wed, 30 Dec 2015 15:04:32 +0300 Subject: [PATCH] improve search with complex specification --- .../analysis/NullRouteSpecification.java | 27 + .../trader/analysis/RouteSpecification.java | 163 +--- .../analysis/RouteSpecificationAndMixer.java | 787 ++++++++++++++++++ .../analysis/RouteSpecificationByPair.java | 123 ++- .../analysis/RouteSpecificationByTarget.java | 33 +- .../analysis/RouteSpecificationByTargets.java | 136 ++- .../analysis/RouteSpecificationMixer.java | 127 +-- .../analysis/RouteSpecificationOrMixer.java | 152 ++++ .../analysis/CrawlerSpecificatorTest.java | 52 ++ 9 files changed, 1232 insertions(+), 368 deletions(-) create mode 100644 core/src/main/java/ru/trader/analysis/NullRouteSpecification.java create mode 100644 core/src/main/java/ru/trader/analysis/RouteSpecificationAndMixer.java create mode 100644 core/src/main/java/ru/trader/analysis/RouteSpecificationOrMixer.java diff --git a/core/src/main/java/ru/trader/analysis/NullRouteSpecification.java b/core/src/main/java/ru/trader/analysis/NullRouteSpecification.java new file mode 100644 index 0000000..27565cd --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/NullRouteSpecification.java @@ -0,0 +1,27 @@ +package ru.trader.analysis; + + +import ru.trader.analysis.graph.Edge; +import ru.trader.analysis.graph.Traversal; + +public class NullRouteSpecification implements RouteSpecification { + @Override + public boolean specified(Edge edge, Traversal entry) { + return false; + } + + @Override + public boolean content(Edge edge, Traversal entry) { + return false; + } + + @Override + public int lastFound(Edge edge, Traversal entry) { + return Integer.MAX_VALUE; + } + + @Override + public int matchCount() { + return 1; + } +} diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecification.java b/core/src/main/java/ru/trader/analysis/RouteSpecification.java index e5f3e94..427e985 100644 --- a/core/src/main/java/ru/trader/analysis/RouteSpecification.java +++ b/core/src/main/java/ru/trader/analysis/RouteSpecification.java @@ -14,162 +14,23 @@ public interface RouteSpecification { public default boolean updateMutated(){return false;} public default boolean mutable(){return false;} public default void update(Traversal entry){} - public default void onAnd(RouteSpecification other){RouteSpecificationMixer.andMix(this, other);} - public default void onOr(RouteSpecification other){} + public default long getStart(){return 0;} + public default long getEnd(){return Long.MAX_VALUE;} default RouteSpecification and(final RouteSpecification other){ - this.onAnd(other); - other.onAnd(this); - return new RouteSpecification() { - @Override - public boolean specified(Edge edge, Traversal entry) { - return RouteSpecification.this.specified(edge, entry) && other.specified(edge, entry); - } - - @Override - public boolean content(Edge edge, Traversal entry) { - return RouteSpecification.this.content(edge, entry) || other.content(edge, entry); - } - - @Override - public int lastFound(Edge edge, Traversal entry) { - return RouteSpecification.this.lastFound(edge, entry) + other.lastFound(edge, entry); - } - - @Override - public int matchCount() { - return RouteSpecification.this.matchCount() + other.matchCount(); - } - - @Override - public boolean updateMutated() { - return RouteSpecification.this.updateMutated() || other.updateMutated(); - } - - @Override - public boolean mutable() { - return RouteSpecification.this.mutable() || other.mutable(); - } - - @Override - public void update(Traversal entry) { - RouteSpecification.this.update(entry); - other.update(entry); - } - - @Override - public void onAnd(RouteSpecification specification) { - RouteSpecification.this.onAnd(specification); - other.onAnd(specification); - } - - @Override - public void onOr(RouteSpecification specification) { - RouteSpecification.this.onOr(specification); - other.onOr(specification); - } - }; + if (other instanceof RouteSpecificationAndMixer){ + ((RouteSpecificationAndMixer) other).mix(this); + return other; + } + return RouteSpecificationAndMixer.mix(this, other); } default RouteSpecification or(final RouteSpecification other){ - this.onOr(other); - return new RouteSpecification() { - @Override - public boolean specified(Edge edge, Traversal entry) { - return RouteSpecification.this.specified(edge, entry) || other.specified(edge, entry); - } - - @Override - public boolean content(Edge edge, Traversal entry) { - return RouteSpecification.this.content(edge, entry) || other.content(edge, entry); - } - - @Override - public int lastFound(Edge edge, Traversal entry) { - return Math.min(RouteSpecification.this.lastFound(edge, entry), other.lastFound(edge, entry)); - } - - @Override - public int matchCount() { - return Math.min(RouteSpecification.this.matchCount(), other.matchCount()); - } - - @Override - public boolean updateMutated() { - return RouteSpecification.this.updateMutated() || other.updateMutated(); - } - @Override - public boolean mutable() { - return RouteSpecification.this.mutable() || other.mutable(); - } - - @Override - public void update(Traversal entry) { - RouteSpecification.this.update(entry); - other.update(entry); - } - - @Override - public void onAnd(RouteSpecification specification) { - RouteSpecification.this.onAnd(specification); - other.onAnd(specification); - } - - @Override - public void onOr(RouteSpecification specification) { - RouteSpecification.this.onOr(specification); - other.onOr(specification); - } - - }; + if (other instanceof RouteSpecificationOrMixer){ + ((RouteSpecificationOrMixer) other).mix(this); + return other; + } + return RouteSpecificationOrMixer.mix(this, other); } - default RouteSpecification negate(){ - return new RouteSpecification() { - @Override - public boolean specified(Edge edge, Traversal entry) { - return !RouteSpecification.this.specified(edge, entry); - } - - - @Override - public boolean content(Edge edge, Traversal entry) { - return !RouteSpecification.this.content(edge, entry); - } - - @Override - public int lastFound(Edge edge, Traversal entry) { - return RouteSpecification.this.lastFound(edge, entry); - } - - @Override - public int matchCount() { - return RouteSpecification.this.matchCount(); - } - - @Override - public boolean updateMutated() { - return RouteSpecification.this.updateMutated(); - } - @Override - public boolean mutable() { - return RouteSpecification.this.mutable(); - } - - @Override - public void update(Traversal entry) { - RouteSpecification.this.update(entry); - } - - @Override - public void onAnd(RouteSpecification specification) { - RouteSpecification.this.onAnd(specification); - } - - @Override - public void onOr(RouteSpecification specification) { - RouteSpecification.this.onOr(specification); - } - }; - } } diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecificationAndMixer.java b/core/src/main/java/ru/trader/analysis/RouteSpecificationAndMixer.java new file mode 100644 index 0000000..3ac6f76 --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/RouteSpecificationAndMixer.java @@ -0,0 +1,787 @@ +package ru.trader.analysis; + +import ru.trader.analysis.graph.Edge; +import ru.trader.analysis.graph.Traversal; + +import java.util.*; + + +public class RouteSpecificationAndMixer implements RouteSpecificationMixer, RouteSpecification { + private final List> specifications; + + public RouteSpecificationAndMixer() { + this.specifications = new ArrayList<>(); + } + + @SafeVarargs + private RouteSpecificationAndMixer(RouteSpecification ... specification) { + this(); + Collections.addAll(specifications, specification); + } + + + @Override + public Collection> getMixed() { + return specifications; + } + + @Override + public boolean specified(Edge edge, Traversal entry) { + for (RouteSpecification specification : specifications) { + if (!specification.specified(edge, entry)) return false; + } + return true; + } + + @Override + public boolean content(Edge edge, Traversal entry) { + for (RouteSpecification specification : specifications) { + if (specification.content(edge, entry)){ + return true; + } + } + return false; + } + + @Override + public int lastFound(Edge edge, Traversal entry) { + int res = 0; + for (RouteSpecification specification : specifications) { + res += specification.lastFound(edge, entry); + } + return res; + } + + @Override + public int matchCount() { + int res = 0; + for (RouteSpecification specification : specifications) { + res += specification.matchCount(); + } + return res; + } + + @Override + public boolean updateMutated() { + for (RouteSpecification specification : specifications) { + if (specification.updateMutated()){ + return true; + } + } + return false; + } + + @Override + public boolean mutable() { + for (RouteSpecification specification : specifications) { + if (specification.mutable()){ + return true; + } + } + return false; + } + + @Override + public void update(Traversal entry) { + specifications.forEach(s -> s.update(entry)); + } + + @Override + public RouteSpecification and(RouteSpecification other) { + return this.mix(other); + } + + @Override + public RouteSpecification mix(RouteSpecification specification) { + if (specification instanceof RouteSpecificationOrMixer){ + RouteSpecificationOrMixer other = (RouteSpecificationOrMixer)specification; + for (RouteSpecification s : specifications) { + other.and(s); + } + return other; + } + if (specification instanceof RouteSpecificationAndMixer){ + Collection> other = ((RouteSpecificationAndMixer)specification).getMixed(); + other.forEach(this::collapse); + } else { + collapse(specification); + } + specifications.sort(RouteSpecificationOrMixer.ROUTE_SPECIFICATION_COMPARATOR); + return this; + } + + private int getMinHope(Collection> specifications){ + if (specifications.isEmpty()) return -1; + return specifications.stream().mapToInt(RouteSpecification::matchCount).sum(); + } + + private void collapse(RouteSpecification specification){ + if (specifications.isEmpty()){ + specifications.add(specification); + return; + } + Collection> res = new ArrayList<>(specifications.size()); + RouteSpecification minSpec = null; + Collection> minCollapsed = null; + int min = -1; + for (RouteSpecification s : specifications) { + Collection> collapsed = andMix(s, specification); + int hopes = getMinHope(collapsed); + if (hopes != -1 && (minCollapsed == null || hopes < min || (hopes == min && minCollapsed.size() RouteSpecification mix(RouteSpecification ... specification){ + RouteSpecificationAndMixer res = new RouteSpecificationAndMixer<>(); + for (RouteSpecification s : specification) { + res.mix(s); + } + return res; + } + + // target fot time t1 A = At1 + // contains A and B and C fot time t1 = [A&B&C]t1 + // target A or B or C fot time t1 = [A|B|C]t1 + // contains A or B or C fot time t1 = [A,B,C]t1 + // pair A or B or C to D fot time t1 = [A,B,C - D]t1 + // contains A from t1 to t2 time = A[t1-t2] + + public static Collection> andMix(RouteSpecificationByTarget spec, RouteSpecification other) { + Collection> res = new ArrayList<>(); + T target = spec.getTarget(); + if (other instanceof RouteSpecificationByTarget){ + RouteSpecificationByTarget os = (RouteSpecificationByTarget)other; + T otherTarget = os.getTarget(); + if (Objects.equals(target, otherTarget)){ + //At1 & At2 -> At1 + long time = Math.min(spec.getEnd(), os.getEnd()); + RouteSpecification newSpec = new RouteSpecificationByTarget<>(target, time); + res.add(newSpec); + } else { + //At1 & Bt2 -> empty + res.add(new NullRouteSpecification<>()); + } + } else + if (other instanceof RouteSpecificationByTargets){ + RouteSpecificationByTargets os = ((RouteSpecificationByTargets)other); + Collection otherTargets = os.getTargets(); + if (otherTargets.contains(target)){ + if (os.isAll()){ + //At1 & [A&B&C]t2 -> At1 & [B&C]t1 + //[A&B&C]t1 & At2 -> At1 & [B&C]t1 + Collection remainTargets = new ArrayList<>(otherTargets); + long time = Math.min(spec.getEnd(), os.getEnd()); + RouteSpecification s1 = new RouteSpecificationByTarget<>(target, time); + res.add(s1); + if (!remainTargets.isEmpty()){ + RouteSpecification s2 = RouteSpecificationByTargets.all(remainTargets, time); + res.add(s2); + } + } else + if (os.isAny()){ + //At1 & [A|B|C]t2 -> At1 + //[A|B|C]t1 & At2 -> At1 + long time = Math.min(spec.getEnd(), os.getEnd()); + RouteSpecification newSpec = new RouteSpecificationByTarget<>(target, time); + res.add(newSpec); + } else { + Collection remainTargets = new ArrayList<>(otherTargets); + remainTargets.remove(target); + if (spec.getEnd() <= os.getEnd()) { + //At1 & [A,B,C]t2 -> At1 + RouteSpecification newSpec = new RouteSpecificationByTarget<>(target, spec.getEnd()); + res.add(newSpec); + } else { + //[A,B,C]t1 & At2 -> At1 | (A[t1-t2] & [B,C]t1) + RouteSpecification s1 = new RouteSpecificationByTarget<>(target, os.getEnd()); + if (!remainTargets.isEmpty()){ + RouteSpecification s2 = RouteSpecificationByTargets.containAny(remainTargets, spec.getEnd(), os.getEnd()); + RouteSpecification s3 = new RouteSpecificationByTarget<>(target, spec.getEnd()); + RouteSpecification newSpec = RouteSpecificationOrMixer.mix(s1, new RouteSpecificationAndMixer<>(s2, s3)); + res.add(newSpec); + } else { + res.add(s1); + } + } + } + } else { + //A & [B,C] -> A & [B,C] + return Collections.emptyList(); + } + } else + if (other instanceof RouteSpecificationByPair){ + RouteSpecificationByPair os = (RouteSpecificationByPair)other; + Collection otherTargets = os.getTargets(); + T otherTarget = os.getTarget(); + if (Objects.equals(target, otherTarget)){ + //At1 & [B,C - A]t2 -> At1 & [B,C]t1 + //[A,B,C - D]t1 & Dt2 -> [A,B,C]t1 & Dt1 + long time = Math.min(spec.getEnd(), os.getEnd()); + RouteSpecification s1 = new RouteSpecificationByTarget<>(target, time); + RouteSpecification s2 = RouteSpecificationByTargets.containAny(otherTargets, time); + res.add(s1); + res.add(s2); + } else + if (otherTargets.contains(target) && spec.getEnd() < os.getEnd()){ + //At1 & [A,B,C - D]t2 -> At1 & [A,B,C - D]t1 + RouteSpecification s2 = new RouteSpecificationByPair<>(otherTargets, otherTarget, spec.getEnd()); + res.add(spec); + res.add(s2); + } else { + //[A,B,C - D]t1 & At2 -> [A,B,C - D]t1 & At2 + //At1 & [B,C - D]t2 -> At1 & [B,C - D]t2 + return Collections.emptyList(); + } + } + return res; + } + + private static void split(Collection source, Collection collection, Collection intersect, Collection retain, Collection retainCollection){ + Collection c = new ArrayList<>(collection); + for (E s : source) { + if (c.remove(s)){ + intersect.add(s); + } else { + retain.add(s); + } + } + retainCollection.addAll(c); + } + + public static Collection> andMix(RouteSpecificationByTargets spec, RouteSpecification other) { + Collection> res = new ArrayList<>(); + Collection targets = spec.getTargets(); + if (other instanceof RouteSpecificationByTarget){ + // already implement in other methods + return andMix((RouteSpecificationByTarget)other, spec); + } else + if (other instanceof RouteSpecificationByTargets){ + RouteSpecificationByTargets os = ((RouteSpecificationByTargets)other); + if (os.getEnd() < spec.getEnd()){ + //swap + return andMix(os, spec); + } + Collection otherTargets = os.getTargets(); + if (spec.isAll()){ + if (os.isAll()){ + Collection retainOtherTargets = new ArrayList<>(otherTargets); + if (retainOtherTargets.removeAll(targets)){ + //[A&B&C]t1 & [B&C&D]t2 -> [A&B&C]t1 & [&D]t2 + RouteSpecification s1 = RouteSpecificationByTargets.all(targets, spec.getEnd()); + res.add(s1); + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s2 = RouteSpecificationByTargets.all(retainOtherTargets, os.getEnd()); + res.add(s2); + } + } else { + //[A&B&C]t1 & [D&E&F]t2 -> [A&B&C]t1 & [D&E&F]t2 + return Collections.emptyList(); + } + } else + if (os.isAny()){ + //[A&B&C]t1 & [B|C|D]t2 -> [&A]t1 & (([&C]t1 & Bt1) | ([&B]t1 & Ct1) | ([&B&C]t1 & [B|C|D][t1-t2])) + Collection intersectTargets = new ArrayList<>(); + Collection retainTargets = new ArrayList<>(); + Collection retainOtherTargets = new ArrayList<>(); + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + if (!retainTargets.isEmpty()){ + RouteSpecification s1 = RouteSpecificationByTargets.all(retainTargets, spec.getEnd()); + res.add(s1); + } + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + for (T intersectTarget : intersectTargets) { + Collection its = new ArrayList<>(intersectTargets); + its.remove(intersectTarget); + RouteSpecification s3 = new RouteSpecificationByTarget<>(intersectTarget, spec.getEnd()); + if (!its.isEmpty()){ + RouteSpecification s2 = RouteSpecificationByTargets.all(its, spec.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + } else { + orSpec.mix(s3); + } + } + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s4 = RouteSpecificationByTargets.all(intersectTargets, spec.getEnd()); + RouteSpecification s5 = RouteSpecificationByTargets.any(otherTargets, spec.getEnd(), os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s4,s5)); + } + res.add(orSpec); + } else { + //[A&B&C]t1 & [D|E|F]t2 -> [A&B&C]t1 & [D|E|F]t2 + return Collections.emptyList(); + } + } else { + if (otherTargets.stream().filter(targets::contains).findAny().isPresent()){ + //[A&B&C]t1 & [B,C,D]t2 -> [A&B&C]t1 + res.add(spec); + } else { + //[A&B&C]t1 & [D,E,F]t2 -> [A&B&C]t1 & [D,E,F]t2 + return Collections.emptyList(); + } + } + } else + if (spec.isAny()){ + if (os.isAll()){ + //[A|B|C]t1 & [B&C&D]t2 + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + //[A|B|C]t1 & [B&C&D]t2 -> [&D]t1 & ((Bt1 & [&C]t1) | (Ct1 & [&B]t1) | [|A]t1 & [B&C]t1)) + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s1 = RouteSpecificationByTargets.all(retainOtherTargets, spec.getEnd()); + res.add(s1); + } + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + for (T intersectTarget : intersectTargets) { + Collection its = new ArrayList<>(intersectTargets); + its.remove(intersectTarget); + RouteSpecification s2 = new RouteSpecificationByTarget<>(intersectTarget, spec.getEnd()); + if (!its.isEmpty()){ + RouteSpecification s3 = RouteSpecificationByTargets.all(its, spec.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + } else { + orSpec.mix(s2); + } + } + if (!retainTargets.isEmpty()){ + RouteSpecification s4 = RouteSpecificationByTargets.any(retainTargets, spec.getEnd()); + RouteSpecification s5 = RouteSpecificationByTargets.all(intersectTargets, spec.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s4,s5)); + } + + res.add(orSpec); + } else { + //[A|B|C]t1 & [D&E&F]t2 -> [A|B|C]t1 & [D&E&F]t2 + return Collections.emptyList(); + } + + } else + if (os.isAny()){ + Collection intersectTarget = new ArrayList<>(otherTargets); + intersectTarget.retainAll(targets); + if (!intersectTarget.isEmpty()){ + //[A|B|C]t1 & [B|C|D]t2 -> [B|C]t1 + RouteSpecification newSpec = RouteSpecificationByTargets.any(intersectTarget, spec.getEnd()); + res.add(newSpec); + } else { + //[A|B|C]t1 & [D|E|F]t2 -> empty + res.add(new NullRouteSpecification<>()); + } + } else { + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + //[A|B|C]t1 & [B,C,D]t2 -> ([B|C]t1) | ([,D]t1 & [|A]t1) + RouteSpecification s1 = RouteSpecificationByTargets.any(intersectTargets, spec.getEnd()); + if (!retainTargets.isEmpty() && !retainOtherTargets.isEmpty()){ + RouteSpecification s2 = RouteSpecificationByTargets.containAny(retainOtherTargets, spec.getEnd()); + RouteSpecification s3 = RouteSpecificationByTargets.any(retainTargets, spec.getEnd()); + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + orSpec.mix(s1); + res.add(orSpec); + } else { + res.add(s1); + } + } else { + //[A|B|C]t1 & [D,E,F]t2 -> [A|B|C]t1 & [D,E,F]t2 + return Collections.emptyList(); + } + } + } else { + if (os.isAll()){ + //[A,B,C]t1 & [B&C&D]t2 + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + //[A,B,C]t1 & [B&C&D]t2 -> [&D]t2 & (([B]t1 & [&C]t2) | ([C]t1 & [&B]t2) | ([A]t1 & [B&C][t1-t2])) + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s1 = RouteSpecificationByTargets.all(retainOtherTargets, os.getEnd()); + res.add(s1); + } + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + for (T intersectTarget : intersectTargets) { + Collection its = new ArrayList<>(intersectTargets); + its.remove(intersectTarget); + RouteSpecification s2 = RouteSpecificationByTargets.containAny(Collections.singleton(intersectTarget), spec.getEnd()); + if (!its.isEmpty()){ + RouteSpecification s3 = RouteSpecificationByTargets.all(its, os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + } else { + orSpec.mix(s2); + } + } + if (!retainTargets.isEmpty()){ + RouteSpecification s4 = RouteSpecificationByTargets.containAny(retainTargets, spec.getEnd()); + RouteSpecification s5 = RouteSpecificationByTargets.all(intersectTargets, spec.getEnd(), os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s4,s5)); + } + + res.add(orSpec); + } else { + //[A,B,C]t1 & [B&C&D]t2 -> [A,B,C]t1 & [D&E&F]t2 + return Collections.emptyList(); + } + + } else + if (os.isAny()){ + //[A,B,C]t1 & [B|C|D]t2 + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + //[A,B,C]t1 & [B|C|D]t2 -> [B|C]t1 | ([A]t1 & ([B|C][t1-t2] | [|D]t2)) + RouteSpecification s1 = RouteSpecificationByTargets.any(intersectTargets, spec.getEnd()); + if (!retainTargets.isEmpty()){ + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(s1); + RouteSpecification s2 = RouteSpecificationByTargets.containAny(retainTargets, spec.getEnd()); + RouteSpecification s3 = RouteSpecificationByTargets.any(intersectTargets, spec.getEnd(), os.getEnd()); + RouteSpecificationAndMixer andSpec; + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s4 = RouteSpecificationByTargets.any(retainOtherTargets, os.getEnd()); + andSpec = new RouteSpecificationAndMixer<>(s2, RouteSpecificationOrMixer.mix(s3, s4)); + } else { + andSpec = new RouteSpecificationAndMixer<>(s2, s3); + } + orSpec.mix(andSpec); + res.add(orSpec); + } else { + res.add(s1); + } + } else { + //[A,B,C]t1 & [D|E|F]t2 -> [A,B,C]t1 & [D|E|F]t2 + return Collections.emptyList(); + } + } else { + //[A,B,C]t1 & [B,C,D]t2 + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + //[A,B,C]t1 & [B,C,D]t2 -> [B,C]t1 | ([A]t1 & [D]t2) + RouteSpecification s1 = RouteSpecificationByTargets.containAny(intersectTargets, spec.getEnd()); + if (!retainTargets.isEmpty() && !retainOtherTargets.isEmpty()){ + RouteSpecification s2 = RouteSpecificationByTargets.containAny(retainTargets, spec.getEnd()); + RouteSpecification s3 = RouteSpecificationByTargets.containAny(retainOtherTargets, os.getEnd()); + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(s1); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + res.add(orSpec); + } else { + res.add(s1); + } + } else { + //[A,B,C]t1 & [D,E,F]t2 -> [A,B,C]t1 & [D,E,F]t2 + return Collections.emptyList(); + } + } + } + } else + if (other instanceof RouteSpecificationByPair){ + // already implement in other methods + return andMix((RouteSpecificationByPair)other, spec); + } + return res; + } + + public static Collection> andMix(RouteSpecificationByPair spec, RouteSpecification other) { + Collection> res = new ArrayList<>(); + Collection targets = spec.getTargets(); + T target = spec.getTarget(); + if (other instanceof RouteSpecificationByTarget){ + // already implement in other methods + return andMix((RouteSpecificationByTarget)other, spec); + } else + if (other instanceof RouteSpecificationByTargets){ + RouteSpecificationByTargets os = ((RouteSpecificationByTargets)other); + Collection otherTargets = os.getTargets(); + + if (os.isAll()){ + //[A,B,C - D]t1 & [B&C&D&E]t2 + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty() || otherTargets.contains(target)){ + if (spec.getEnd() <= os.getEnd()){ + //[A,B,C - D]t1 & [B&C&D&E]t2 -> [&E]t2 & (([B - D]t1 & [&C]t2) | ([C - D]t1 & [&B]t2) | ([A - D]t1 & [B&C]t2)) + //[A,B,C - F]t1 & [B&C&D&E]t2 -> [D&E]t2 & (([B - F]t1 & [&C]t2) | ([C - F]t1 & [&B]t2) | ([A - F]t1 & [B&C]t2) + retainOtherTargets.remove(target); + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s1 = RouteSpecificationByTargets.all(retainOtherTargets, os.getEnd()); + res.add(s1); + } + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + for (T intersectTarget : intersectTargets) { + Collection its = new ArrayList<>(intersectTargets); + its.remove(intersectTarget); + RouteSpecification s2 = new RouteSpecificationByPair<>(Collections.singleton(intersectTarget), target, spec.getEnd()); + if (!its.isEmpty()){ + RouteSpecification s3 = RouteSpecificationByTargets.all(its, os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + } else { + orSpec.mix(s2); + } + } + if (!retainTargets.isEmpty()){ + RouteSpecification s4 = new RouteSpecificationByPair<>(retainTargets, target, spec.getEnd()); + if (!intersectTargets.isEmpty()){ + RouteSpecification s5 = RouteSpecificationByTargets.all(intersectTargets, os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s4,s5)); + } else { + orSpec.mix(s4); + } + } + res.add(orSpec); + } else { + //[B,C,D,E - A]t2 & [A&B&C]t1 -> ([&B]t1 & [C - A]t1) | ([&C]t1 & [B - A]t1) | ([B&C]t1 & [D,E - A]t1) | ([A&B&C]t1 & [A][t1-t2]) + //[B,C,D - E]t2 & [A&B&C]t1 -> [&A]t1 & (([&B]t1 & [C - E]t1) | ([&C]t1 & [B - E]t1) | ([B&C]t1 & [D - E]t1) | ([B&C]t1 & [E][t1-t2])) + retainOtherTargets.remove(target); + if (!retainOtherTargets.isEmpty()){ + RouteSpecification s1 = RouteSpecificationByTargets.all(retainOtherTargets, os.getEnd()); + res.add(s1); + } + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + for (T intersectTarget : intersectTargets) { + Collection its = new ArrayList<>(intersectTargets); + its.remove(intersectTarget); + RouteSpecification s2 = new RouteSpecificationByPair<>(Collections.singleton(intersectTarget), target, os.getEnd()); + if (!its.isEmpty()){ + RouteSpecification s3 = RouteSpecificationByTargets.all(its, os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + } else { + orSpec.mix(s2); + } + } + if (!retainTargets.isEmpty()){ + RouteSpecification s4 = new RouteSpecificationByPair<>(retainTargets, target, os.getEnd()); + RouteSpecification s5 = RouteSpecificationByTargets.all(intersectTargets, os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s4,s5)); + } + if (!intersectTargets.isEmpty()){ + Collection targets6 = new ArrayList<>(intersectTargets); + if (otherTargets.contains(target)) targets6.add(target); + RouteSpecification s6 = RouteSpecificationByTargets.all(targets6, os.getEnd()); + RouteSpecification s7 = RouteSpecificationByTargets.containAny(Collections.singleton(target), os.getEnd(), spec.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s6,s7)); + } + res.add(orSpec); + } + } else { + //[A,B,C - D]t1 & [E&F]t2 -> [A,B,C - D]t1 & [E&F]t2 + return Collections.emptyList(); + } + } else + if (os.isAny()){ + long time = Math.min(spec.getEnd(), os.getEnd()); + if (otherTargets.contains(target)){ + //[A,B,C,F - D]t1 & [B|C|D|E]t2 -> ([A,B,C,F]t1 & Dt1) | ([A,B,C,F - D]t1 & [B|C|E]t2) + //[A,B,C,F - D]t2 & [B|C|D|E]t1 -> ([A,B,C,F]t1 & Dt1) | ([A,B,C,F - D]t1 & [B|C|E]t1) + RouteSpecification s1 = RouteSpecificationByTargets.containAny(targets, time); + RouteSpecification s2 = new RouteSpecificationByTarget<>(target, time); + Collection retainOtherTargets = new ArrayList<>(otherTargets); // B,C,E + retainOtherTargets.remove(target); + if (!retainOtherTargets.isEmpty()){ + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(new RouteSpecificationAndMixer<>(s1,s2)); + RouteSpecification s3 = new RouteSpecificationByPair<>(targets, target, time); + RouteSpecification s4 = RouteSpecificationByTargets.any(retainOtherTargets, os.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s3,s4)); + } else { + res.add(s1); + res.add(s2); + } + } else { + //[A,B,C,D - F]t1 & [B|C|E]t2 -> [A,B,C,D - F]t1 & [B|C|E]t2 + //[A,B,C,D - F]t2 & [B|C|E]t1 -> [A,B,C,D - F]t1 & [B|C|E]t1 + //[A,B,C - D] & [E|F] -> [A,B,C - D] & [E|F] + RouteSpecification s1 = new RouteSpecificationByPair<>(targets, target, time); + res.add(s1); + res.add(os); + } + } else { + if (otherTargets.contains(target)){ + //[A,B,C - D]t1 & [B,C,D,E]t2 -> [A,B,C - D]t1 + if (spec.getEnd() <= os.getEnd()){ + res.add(spec); + } else { + Collection intersectTargets = new ArrayList<>(targets); //B,C + intersectTargets.retainAll(otherTargets); + if (!intersectTargets.isEmpty()){ + //[B,C,D - A]t2 & [A,B,C]t1 -> [B,C,D - A]t1 | ([B,C]t1 & [A][t1-t2]) + RouteSpecification s1 = new RouteSpecificationByPair<>(targets, target, os.getEnd()); + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(s1); + RouteSpecification s2 = RouteSpecificationByTargets.containAny(intersectTargets, os.getEnd()); + RouteSpecification s3 = RouteSpecificationByTargets.containAny(Collections.singleton(target), os.getEnd(), spec.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + res.add(orSpec); + } else { + //[B,C,D - A]t2 & [A,E,F]t1 -> [B,C,D - A]t1 | ([E,F]t1 & [B,C,D - A][t1-t2]) + RouteSpecification s1 = new RouteSpecificationByPair<>(targets, target, os.getEnd()); + Collection retainOtherTargets = new ArrayList<>(otherTargets); + retainOtherTargets.remove(target); + if (!retainOtherTargets.isEmpty()){ + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(s1); + RouteSpecification s2 = RouteSpecificationByTargets.containAny(otherTargets, os.getEnd()); + RouteSpecification s3 = new RouteSpecificationByPair<>(targets, target, os.getEnd(), spec.getEnd()); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + res.add(orSpec); + } else { + res.add(s1); + } + } + } + } else { + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D,E + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + //[A,B,C - F]t1 & [B,C,D,E]t2 -> [B,C - F]t1 | ([A - F]t1 & [D,E]t2) + //[B,C,D - E]t2 & [A,B,C]t1 -> [B,C - E]t1 | ([D - E]t2 & [,A]t1 ) + long time = Math.min(spec.getEnd(), os.getEnd()); + RouteSpecification s1 = new RouteSpecificationByPair<>(intersectTargets, target, time); + if (!retainTargets.isEmpty() && !retainOtherTargets.isEmpty()){ + RouteSpecification s2 = new RouteSpecificationByPair<>(retainTargets, target, spec.getEnd()); + RouteSpecification s3 = RouteSpecificationByTargets.containAny(retainOtherTargets, os.getEnd()); + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(s1); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + res.add(orSpec); + } else { + res.add(s1); + } + } else { + //[A,B,C - F]t1 & [G,H,I]t2 -> [A,B,C - F]t1 & [G,H,I]t2 + return Collections.emptyList(); + } + } + } + } else + if (other instanceof RouteSpecificationByPair){ + RouteSpecificationByPair os = ((RouteSpecificationByPair)other); + Collection otherTargets = os.getTargets(); + T otherTarget = os.getTarget(); + if (os.getEnd() < spec.getEnd()){ + return andMix(os, spec); + } + if (target.equals(otherTarget)){ + //[A,B,C - F]t1 & [B,C,D - F]t2 -> [B,C - F]t1 | ([A - F]t1 & [D - F]t2) + Collection intersectTargets = new ArrayList<>(); //B,C + Collection retainTargets = new ArrayList<>(); //A + Collection retainOtherTargets = new ArrayList<>(); //D + split(targets, otherTargets, intersectTargets, retainTargets, retainOtherTargets); + if (!intersectTargets.isEmpty()){ + RouteSpecification s1 = new RouteSpecificationByPair<>(intersectTargets, target, spec.getEnd()); + if (!retainTargets.isEmpty() && !retainOtherTargets.isEmpty()){ + RouteSpecification s2 = new RouteSpecificationByPair<>(retainTargets, target, spec.getEnd()); + RouteSpecification s3 = new RouteSpecificationByPair<>(retainOtherTargets, target, os.getEnd()); + RouteSpecificationOrMixer orSpec = new RouteSpecificationOrMixer<>(); + orSpec.mix(s1); + orSpec.mix(new RouteSpecificationAndMixer<>(s2,s3)); + res.add(orSpec); + } else { + res.add(s1); + } + } else { + //[A,B,C - F]t1 & [D,E - F]t2 -> [A,B,C - F]t1 & [D,E - F]t2 + return Collections.emptyList(); + } + } else { + //[A,B,C - D]t1 & [B,C,D - E]t2 -> [B,C - D]t1 & [- E]) | ([A - D] & [B,C - E]) + //[A,B,C - E]t1 & [B,C,D - A]t2 -> ([B,C - D - E]) | ([A - D] & [B,C - E]) + //[A,B,C - D]t1 & [B,C,E - G]t2 -> ([B,C - D - E]) | ([A - D] & [B,C - E]) + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } + return res; + } + + public static Collection> andMix(RouteSpecification spec, RouteSpecification other) { + if (spec instanceof NullRouteSpecification || other instanceof NullRouteSpecification){ + RouteSpecification res = new NullRouteSpecification<>(); + return Collections.singleton(res); + } + Collection> res = Collections.emptyList(); + RouteSpecification first = spec; + RouteSpecification second = other; + RouteSpecification adding = null; + if (spec.getStart() != other.getStart()){ + if (spec.getStart() > other.getStart()){ + first = other; + second = spec; + } + if (first.getEnd() > second.getStart()){ + adding = clone(first, first.getStart(), second.getStart()); + if (adding != null){ + first = clone(first, second.getStart(), first.getEnd()); + } + } + else { + return Collections.emptyList(); + } + } + if (first instanceof RouteSpecificationByTarget){ + res = andMix((RouteSpecificationByTarget)first, second); + } + if (first instanceof RouteSpecificationByTargets){ + res = andMix((RouteSpecificationByTargets)first, second); + } + if (first instanceof RouteSpecificationByPair){ + res = andMix((RouteSpecificationByPair)first, second); + } + if (!res.isEmpty() && adding != null){ + res.add(new RouteSpecificationAndMixer<>(adding, second)); + } + return res; + } + + private static RouteSpecification clone(RouteSpecification specification, long startTime, long endTime){ + if (specification instanceof RouteSpecificationByTarget){ + RouteSpecificationByTarget spec = (RouteSpecificationByTarget) specification; + return new RouteSpecificationByTarget<>(spec.getTarget(), startTime, endTime); + } + if (specification instanceof RouteSpecificationByTargets){ + RouteSpecificationByTargets spec = (RouteSpecificationByTargets) specification; + if (spec.isAll()) return RouteSpecificationByTargets.all(spec.getTargets(), startTime, endTime); + if (spec.isAny()) return RouteSpecificationByTargets.any(spec.getTargets(), startTime, endTime); + return RouteSpecificationByTargets.containAny(spec.getTargets(), startTime, endTime); + } + if (specification instanceof RouteSpecificationByPair){ + RouteSpecificationByPair spec = (RouteSpecificationByPair) specification; + return new RouteSpecificationByPair<>(spec.getTargets(), spec.getTarget(), startTime, endTime); + } + return null; + } +} \ No newline at end of file diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecificationByPair.java b/core/src/main/java/ru/trader/analysis/RouteSpecificationByPair.java index 2f9e81c..6ea9467 100644 --- a/core/src/main/java/ru/trader/analysis/RouteSpecificationByPair.java +++ b/core/src/main/java/ru/trader/analysis/RouteSpecificationByPair.java @@ -3,100 +3,131 @@ package ru.trader.analysis; import ru.trader.analysis.graph.Edge; import ru.trader.analysis.graph.Traversal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; +import java.util.*; public class RouteSpecificationByPair implements RouteSpecification { - protected final Collection first; - protected final T second; - protected final long time; - private boolean checkSecond; + private final Collection first; + private final T second; + private final long start; + private final long end; public RouteSpecificationByPair(T first, T second) { this(first, second, Long.MAX_VALUE); } public RouteSpecificationByPair(T first, T second, long time) { - this.first = new ArrayList<>(); - this.first.add(first); - this.second = second; - this.time = time; - checkSecond = true; + this(Collections.singleton(first), second, 0, time); } public RouteSpecificationByPair(Collection first, T second) { - this(first, second, Long.MAX_VALUE); + this(first, second, 0, Long.MAX_VALUE); } public RouteSpecificationByPair(Collection first, T second, long time) { - this.first = new ArrayList<>(first); + this(first, second, 0, time); + } + + public RouteSpecificationByPair(Collection first, T second, long startTime, long endTime) { + this.first = new HashSet<>(first); this.second = second; - this.time = time; - checkSecond = true; + this.start = startTime; + this.end = endTime; + + } + + public T getTarget(){ + return second; + } + + public Collection getTargets(){ + return first; + } + + @Override + public long getStart() { + return start; + } + + @Override + public long getEnd() { + return end; } private boolean checkTime(){ - return time < Long.MAX_VALUE; - } - - protected void remove(){ - checkSecond = false; + return end < Long.MAX_VALUE; } @Override public boolean specified(Edge edge, Traversal entry) { - return searchPair(edge, entry) == 0; + return searchPair(edge, entry, false) == 0; } @Override public boolean content(Edge edge, Traversal entry) { - if (checkTime() && edge.getTime() + entry.getTime() > time) return false; + if (checkTime()){ + long time = edge.getTime(); + if (time < start || time > end) return false; + } T obj = edge.getTarget().getEntry(); return second.equals(obj) || first.contains(obj); } @Override public int lastFound(Edge edge, Traversal entry) { - return searchPair(edge, entry); + return searchPair(edge, entry, true); } @Override public int matchCount() { - return checkSecond ? 2 : 1; + return 2; } - private int searchPair(Edge edge, Traversal entry){ + private int searchPair(Edge edge, Traversal entry, boolean full){ int fIndex = -1; int sIndex = -1; - List> entries = entry.toList(); - int max = entries.size(); - for (int i = 0; i < max; i++) { - Traversal e = entries.get(i); + + T obj = edge.getTarget().getEntry(); + boolean check = true; + if (checkTime()){ + long time = edge.getTime(); + if (time < start || time > end) check = false; + } + if (check){ + if (second.equals(obj)){ + sIndex = 0; + } else + if (first.contains(obj)){ + fIndex = 0; + } + } + + int i = 0; + Optional> t = Optional.of(entry); + while (t.isPresent()){ + i++; + Traversal e = t.get(); + t = e.getHead(); + if (checkTime()){ + long time = e.getTime(); + if (time > end) continue; + if (time < start) break; + } T target = e.getTarget().getEntry(); - if (second.equals(target)){ - if (checkTime() && e.getTime() > time) return checkSecond ? 2 : 1; + if (sIndex == -1 && second.equals(target)){ sIndex = i; } - if (sIndex != -1 && fIndex != -1 && fIndex <= sIndex){ + //low index last + if (sIndex != -1 && fIndex != -1 && fIndex >= sIndex){ return 0; } - if (fIndex == -1 && first.contains(target)){ + if ((full || sIndex != -1) && fIndex == -1 && first.contains(target)){ fIndex = i; } } - T obj = edge.getTarget().getEntry(); - if (fIndex == -1 && first.contains(obj)){ - fIndex = max; - } - if (second.equals(obj)){ - if (checkTime() && edge.getTime() + entry.getTime() > time) return checkSecond ? 2 : 1; - sIndex = max; - } - if (fIndex == -1) return checkSecond ? 2 : 1; - if (sIndex == -1) return checkSecond ? 1 : 0; - return fIndex <= sIndex ? 0 : 1; + + if (sIndex == -1 && fIndex != -1) return 1; + if (fIndex == -1) return 2; + return fIndex >= sIndex ? 0 : 1; } } diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecificationByTarget.java b/core/src/main/java/ru/trader/analysis/RouteSpecificationByTarget.java index 7f29b05..78338d6 100644 --- a/core/src/main/java/ru/trader/analysis/RouteSpecificationByTarget.java +++ b/core/src/main/java/ru/trader/analysis/RouteSpecificationByTarget.java @@ -4,34 +4,49 @@ import ru.trader.analysis.graph.Edge; import ru.trader.analysis.graph.Traversal; public class RouteSpecificationByTarget implements RouteSpecification { - private T target; - protected final long time; + private final T target; + private final long start; + private final long end; public RouteSpecificationByTarget(T target) { - this(target, Long.MAX_VALUE); + this(target, 0, Long.MAX_VALUE); } public RouteSpecificationByTarget(T target, long time) { + this(target, 0, time); + } + + public RouteSpecificationByTarget(T target, long startTime, long endTime) { this.target = target; - this.time = time; + this.start = startTime; + this.end = endTime; } private boolean checkTime(){ - return time < Long.MAX_VALUE; + return end < Long.MAX_VALUE; } - protected T getTarget(){ + public T getTarget(){ return target; } - protected void remove(){ - target = null; + @Override + public long getStart(){ + return start; + } + + @Override + public long getEnd(){ + return end; } @Override public boolean specified(Edge edge, Traversal entry) { if (target == null) return true; - if (checkTime() && edge.getTime() + entry.getTime() > time) return false; + if (checkTime()){ + long time = edge.getTime(); + if (time < start || time > end) return false; + } return edge.isConnect(target); } } diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecificationByTargets.java b/core/src/main/java/ru/trader/analysis/RouteSpecificationByTargets.java index e725184..51a558d 100644 --- a/core/src/main/java/ru/trader/analysis/RouteSpecificationByTargets.java +++ b/core/src/main/java/ru/trader/analysis/RouteSpecificationByTargets.java @@ -6,39 +6,76 @@ import ru.trader.analysis.graph.Traversal; import java.util.*; public class RouteSpecificationByTargets implements RouteSpecification { - protected final Collection targets; - protected final boolean all; - protected final boolean targetOnly; - protected final long time; + private final Collection targets; + private final boolean all; + private final boolean targetOnly; + private final long start; + private final long end; - protected RouteSpecificationByTargets(Collection targets, long time, boolean all, boolean targetOnly) { + private RouteSpecificationByTargets(Collection targets, long startTime, long endTime, boolean all, boolean targetOnly) { this.all = all; this.targetOnly = targetOnly; this.targets = new HashSet<>(targets); - this.time = time; + this.start = startTime; + this.end = endTime; } private boolean checkTime(){ - return time < Long.MAX_VALUE; + return end < Long.MAX_VALUE; + } + + public Collection getTargets(){ + return targets; + } + + @Override + public long getStart() { + return start; + } + + @Override + public long getEnd() { + return end; + } + + public boolean isAny(){ + return targetOnly; + } + + public boolean isAll(){ + return all; + } + + public boolean isContainAny(){ + return !targetOnly && !all; } @Override public boolean specified(Edge edge, Traversal entry) { if (targets.isEmpty()) return true; - if (checkTime() && targetOnly && edge.getTime() + entry.getTime() > time) return false; + if (checkTime()){ + long time = edge.getTime(); + if (targetOnly && (time < start || time > end)) return false; + } return all ? containsAll(edge, entry) == 0 : containsAny(edge, entry) == 0; } @Override public boolean content(Edge edge, Traversal entry) { - if (checkTime() && edge.getTime() + entry.getTime() > time) return false; + if (checkTime()){ + long time = edge.getTime(); + if (time < start || time > end) return false; + } return targets.contains(edge.getTarget().getEntry()); } @Override public int lastFound(Edge edge, Traversal entry) { if (targets.isEmpty()) return 0; - if (checkTime() && targetOnly && edge.getTime() + entry.getTime() > time) return matchCount(); + if (checkTime()){ + long time = edge.getTime(); + if (targetOnly && (time < start || time > end)) return matchCount(); + } return all ? containsAll(edge, entry) : containsAny(edge, entry); } @@ -50,51 +87,62 @@ public class RouteSpecificationByTargets implements RouteSpecification { private int containsAll(Edge edge, Traversal entry) { Collection founds = new HashSet<>(); - List> entries = entry.toList(); - for (Traversal e : entries) { + + Optional> t = Optional.of(entry); + while (t.isPresent()){ + Traversal e = t.get(); + t = e.getHead(); + if (checkTime()){ + long time = e.getTime(); + if (time > end) continue; + if (time < start) break; + } T target = e.getTarget().getEntry(); if (targets.contains(target)){ - if (checkTime() && e.getTime() > time){ - return targets.size() - founds.size(); - } else { - founds.add(target); - } + founds.add(target); } if (targets.size() == founds.size()) return 0; } + T target = edge.getTarget().getEntry(); + if (checkTime()){ + long time = edge.getTime(); + if (time < start || time > end) return targets.size() - founds.size(); + } if (targets.contains(target)){ - if (checkTime() && edge.getTime() + entry.getTime() > time){ - return targets.size() - founds.size(); - } else { - founds.add(target); - } + founds.add(target); } return targets.size() - founds.size(); } private int containsAny(Edge edge, Traversal entry) { T obj = edge.getTarget().getEntry(); - if (targets.contains(obj)){ - if (targetOnly){ - if (checkTime() && edge.getTime() + entry.getTime() > time) return 1; - else return 0; - } else { - return 0; + boolean check = true; + if (checkTime()){ + long time = edge.getTime(); + if (time < start || time > end){ + if (targetOnly) return 1; + check = false; } } + if (check && targets.contains(obj)){ + return 0; + } if (targetOnly){ return 1; } - List> entries = entry.toList(); - for (Traversal e : entries) { + Optional> t = Optional.of(entry); + while (t.isPresent()){ + Traversal e = t.get(); + t = e.getHead(); + if (checkTime()){ + long time = e.getTime(); + if (time > end) continue; + if (time < start) break; + } T target = e.getTarget().getEntry(); if (targets.contains(target)){ - if (checkTime() && e.getTime() > time){ - return 1; - } else { - return 0; - } + return 0; } } return 1; @@ -105,7 +153,11 @@ public class RouteSpecificationByTargets implements RouteSpecification { } public static RouteSpecificationByTargets all(Collection targets, long time){ - return new RouteSpecificationByTargets<>(targets, time, true, false); + return new RouteSpecificationByTargets<>(targets, 0, time, true, false); + } + + public static RouteSpecificationByTargets all(Collection targets, long startTime, long endTime){ + return new RouteSpecificationByTargets<>(targets, startTime, endTime, true, false); } public static RouteSpecificationByTargets any(Collection targets){ @@ -113,7 +165,11 @@ public class RouteSpecificationByTargets implements RouteSpecification { } public static RouteSpecificationByTargets any(Collection targets, long time){ - return new RouteSpecificationByTargets<>(targets, time, false, true); + return new RouteSpecificationByTargets<>(targets, 0, time, false, true); + } + + public static RouteSpecificationByTargets any(Collection targets, long startTime, long endTime){ + return new RouteSpecificationByTargets<>(targets, startTime, endTime, false, true); } public static RouteSpecificationByTargets containAny(Collection targets){ @@ -121,6 +177,10 @@ public class RouteSpecificationByTargets implements RouteSpecification { } public static RouteSpecificationByTargets containAny(Collection targets, long time){ - return new RouteSpecificationByTargets<>(targets, time, false, false); + return new RouteSpecificationByTargets<>(targets, 0, time, false, false); + } + + public static RouteSpecificationByTargets containAny(Collection targets, long startTime, long endTime){ + return new RouteSpecificationByTargets<>(targets, startTime, endTime, false, false); } } diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecificationMixer.java b/core/src/main/java/ru/trader/analysis/RouteSpecificationMixer.java index 8fccb2c..3537579 100644 --- a/core/src/main/java/ru/trader/analysis/RouteSpecificationMixer.java +++ b/core/src/main/java/ru/trader/analysis/RouteSpecificationMixer.java @@ -2,130 +2,9 @@ package ru.trader.analysis; import java.util.Collection; -public class RouteSpecificationMixer { - public static void andMix(RouteSpecificationByTargets spec, RouteSpecification other) { - if (other instanceof RouteSpecificationByTarget){ - RouteSpecificationByTarget os = (RouteSpecificationByTarget)other; - T otherTarget = os.getTarget(); - if (os.time < spec.time){ - spec.targets.remove(otherTarget); - } else { - if (spec.targetOnly && spec.targets.contains(otherTarget)){ - os.remove(); - } - } - } else - if (other instanceof RouteSpecificationByTargets){ - RouteSpecificationByTargets os = ((RouteSpecificationByTargets)other); - if (os.all){ - Collection otherTargets = os.targets; - if (os.time < spec.time){ - spec.targets.removeAll(otherTargets); - } else { - if (spec.all){ - os.targets.removeAll(spec.targets); - } - } - } - } else - if (other instanceof RouteSpecificationByPair){ - RouteSpecificationByPair os = (RouteSpecificationByPair)other; - T otherTarget = os.second; - if (os.time < spec.time){ - spec.targets.remove(otherTarget); - } else { - if (spec.targetOnly && spec.targets.contains(otherTarget)){ - os.remove(); - } - } - } - } - - public static void andMix(RouteSpecificationByTarget spec, RouteSpecification other) { - if (other instanceof RouteSpecificationByTarget){ - RouteSpecificationByTarget os = (RouteSpecificationByTarget)other; - T otherTarget = os.getTarget(); - if (spec.getTarget() == otherTarget){ - if (os.time < spec.time){ - spec.remove(); - } else { - os.remove(); - } - } - } else - if (other instanceof RouteSpecificationByTargets){ - RouteSpecificationByTargets os = ((RouteSpecificationByTargets)other); - if (os.all){ - Collection otherTargets = os.targets; - if (otherTargets.contains(spec.getTarget())){ - if (os.time < spec.time){ - spec.remove(); - } else { - os.targets.remove(spec.getTarget()); - } - } - } - } else - if (other instanceof RouteSpecificationByPair){ - RouteSpecificationByPair os = (RouteSpecificationByPair)other; - T otherTarget = os.second; - if (spec.getTarget() == otherTarget){ - if (os.time < spec.time){ - spec.remove(); - } else { - os.remove(); - } - } - } - } - - public static void andMix(RouteSpecificationByPair spec, RouteSpecification other) { - if (other instanceof RouteSpecificationByTarget){ - RouteSpecificationByTarget os = (RouteSpecificationByTarget)other; - T otherTarget = os.getTarget(); - if (spec.second == otherTarget){ - if (os.time >= spec.time) { - os.remove(); - } - } - } else - if (other instanceof RouteSpecificationByTargets){ - RouteSpecificationByTargets os = ((RouteSpecificationByTargets)other); - if (os.targetOnly){ - Collection otherTargets = os.targets; - if (otherTargets.contains(spec.second)){ - if (os.time >= spec.time) { - os.targets.remove(spec.second); - } - } - } - } else - if (other instanceof RouteSpecificationByPair){ - RouteSpecificationByPair os = (RouteSpecificationByPair)other; - boolean eqFirst = spec.first.containsAll(os.first); - T otherTarget = os.second; - if (eqFirst && spec.second == otherTarget){ - if (os.time < spec.time){ - spec.remove(); - } else { - os.remove(); - } - } - } - } - - public static void andMix(RouteSpecification spec, RouteSpecification other) { - if (spec instanceof RouteSpecificationByTarget){ - andMix((RouteSpecificationByTarget)spec, other); - } else - if (spec instanceof RouteSpecificationByTargets){ - andMix((RouteSpecificationByTargets)spec, other); - } else - if (spec instanceof RouteSpecificationByPair){ - andMix((RouteSpecificationByPair)spec, other); - } - - } +public interface RouteSpecificationMixer { + RouteSpecification mix(RouteSpecification specification); + Collection> getMixed(); } \ No newline at end of file diff --git a/core/src/main/java/ru/trader/analysis/RouteSpecificationOrMixer.java b/core/src/main/java/ru/trader/analysis/RouteSpecificationOrMixer.java new file mode 100644 index 0000000..6283c97 --- /dev/null +++ b/core/src/main/java/ru/trader/analysis/RouteSpecificationOrMixer.java @@ -0,0 +1,152 @@ +package ru.trader.analysis; + +import ru.trader.analysis.graph.Edge; +import ru.trader.analysis.graph.Traversal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +public class RouteSpecificationOrMixer implements RouteSpecificationMixer, RouteSpecification { + + private final List> specifications; + + public RouteSpecificationOrMixer() { + this.specifications = new ArrayList<>(); + } + + @Override + public Collection> getMixed() { + return specifications; + } + + @Override + public boolean specified(Edge edge, Traversal entry) { + for (RouteSpecification specification : specifications) { + if (specification.specified(edge, entry)) return true; + } + return false; + } + + @Override + public boolean content(Edge edge, Traversal entry) { + for (RouteSpecification specification : specifications) { + if (specification.content(edge, entry)){ + return true; + } + } + return false; + } + + @Override + public int lastFound(Edge edge, Traversal entry) { + int res = Integer.MAX_VALUE; + for (RouteSpecification specification : specifications) { + res = Math.min(res, specification.lastFound(edge, entry)); + } + return res; + } + + @Override + public int matchCount() { + int res = Integer.MAX_VALUE; + for (RouteSpecification specification : specifications) { + res = Math.min(res, specification.matchCount()); + } + return res; + } + + @Override + public boolean updateMutated() { + for (RouteSpecification specification : specifications) { + if (specification.updateMutated()){ + return true; + } + } + return false; + } + + @Override + public boolean mutable() { + for (RouteSpecification specification : specifications) { + if (specification.mutable()){ + return true; + } + } + return false; + } + + @Override + public void update(Traversal entry) { + specifications.forEach(s -> s.update(entry)); + } + + @Override + public RouteSpecification and(RouteSpecification specification) { + Collection> res = new ArrayList<>(specifications.size()); + for (RouteSpecification s : specifications) { + res.add(RouteSpecificationAndMixer.mix(s, specification)); + } + specifications.clear(); + specifications.addAll(res); + return this; + } + + @Override + public RouteSpecification or(RouteSpecification other) { + return this.mix(other); + } + + @Override + public RouteSpecification mix(RouteSpecification specification) { + if (specification instanceof NullRouteSpecification) return this; + specifications.add(specification); + specifications.sort(ROUTE_SPECIFICATION_COMPARATOR); + return this; + } + + @SafeVarargs + public static RouteSpecification mix(RouteSpecification ... specification){ + RouteSpecificationOrMixer res = new RouteSpecificationOrMixer<>(); + for (RouteSpecification s : specification) { + res.mix(s); + } + return res; + } + + + public final static RouteSpecificationComparator ROUTE_SPECIFICATION_COMPARATOR = new RouteSpecificationComparator(); + + private static class RouteSpecificationComparator implements Comparator { + + private int getHard(RouteSpecification specification){ + if (specification instanceof RouteSpecificationByTarget){ + return 1; + } + if (specification instanceof RouteSpecificationByTargets){ + RouteSpecificationByTargets s = (RouteSpecificationByTargets) specification; + if (s.isAny()){ + return 2; + } + return s.isContainAny() ? 3 : 5; + } + if (specification instanceof RouteSpecificationByPair){ + return 4; + } + if (specification instanceof LoopRouteSpecification){ + return 4; + } + return 6; + } + + @Override + public int compare(RouteSpecification o1, RouteSpecification o2) { + int hard1 = getHard(o1); + int hard2 = getHard(o2); + int cmp = Integer.compare(hard1, hard2); + if (cmp != 0) return cmp; + return Integer.compare(o1.matchCount(), o2.matchCount()); + } + } +} \ No newline at end of file diff --git a/core/src/test/java/ru/trader/analysis/CrawlerSpecificatorTest.java b/core/src/test/java/ru/trader/analysis/CrawlerSpecificatorTest.java index dc35d8b..c084d9c 100644 --- a/core/src/test/java/ru/trader/analysis/CrawlerSpecificatorTest.java +++ b/core/src/test/java/ru/trader/analysis/CrawlerSpecificatorTest.java @@ -213,4 +213,56 @@ public class CrawlerSpecificatorTest extends Assert{ paths.clear(); } + @Test + public void testMix() throws Exception { + LOG.info("Test Mix"); + + // target A = A + // contains A and B and C = [A&B&C] + // target A or B or C = [A|B|C] + // contains A or B or C = [A,B,C] + // pair A or B or C to D = [A,B,C - D] + + //A & A -> A + //A & [A,B,C] -> A + //A & [B,C] -> A & [B,C] + //A & [A|B|C] -> A + //A & [A&B&C] -> A & [B&C] + //A & [A,B,C - D] -> A & [A,B,C - D] + //A & [B,C - A] -> A & [B,C] + + //[A,B,C] & A -> A & [B,C] + //[A,B,C] & [B,C,D] -> [A,B,C,D] + //[A,B,C] & [B|C|D] -> [B|C] | ([A] & D) + //[A,B,C] & [B&C&D] -> [B&C&D] + //[A,B,C] & [B,C,D - E] -> [B,C - E] | ([A] & [D - E]) + //[A,B,C] & [B,C,D - A] -> [B,C,D - A] + + //[A|B|C] & A -> A + //[A|B|C] & [B,C,D] -> ([D] & A) | ([B|C]) + //[A|B|C] & [B|C|D] -> [A|B|C|D] + //[A|B|C] & [B&C&D] -> (A & [B&C]) | (B & [C&D]) | (C & [B&D]) + //[A|B|C] & [B,C,D - E] -> [A|B|C] & [B,C,D - E] + //[A|B|C] & [B,C,D - A] -> (A & [B,C,D]) | ([B|C] & [B,C,D - A]) + + //[A&B&C] & A -> A & [B&C] + //[A&B&C] & [B,C,D] -> [A&B&C] + //[A&B&C] & [B|C|D] -> ([A&C] & B) | ([A&B] & C) | ([A&B&C] & D) + //[A&B&C] & [B&C&D] -> [A&B&C&D] + //[A&B&C] & [B,C,D - E] -> ([A&C] & [B - E]) | ([A&B] & [C - E]) | ([A&B&C] & [D - E]) + //[A&B&C] & [B,C,D,E - A] -> ([B&C] & [D,E - A]) | ([C] & [B - A]) + + //[A,B,C - D] & A -> [A,B,C - D] & A + //[A,B,C - D] & D -> [A,B,C] & D + //[A,B,C - D] & [B,C,D,E] -> [A,B,C - D] + //[A,B,C - F] & [B,C,D,E] -> ([A - F] & [D,E]) | [B,C - F] + //[A,B,C - D] & [B|C|D|E] -> ([A,B,C - D] & D) | ([A,B,C - D] & [B|C|E]) + //[A,B,C - F] & [B|C|D|E] -> [A,B,C - F] & [B|C|D|E] + //[A,B,C - D] & [B&C&D&E] -> ([B - D] & [C&E]) | ([C - D] & [B&E]) | ([A - D] & [B&C&E]) + //[A,B,C - F] & [B&C&D&E] -> ([B - F] & [C&D&E]) | ([C - F] & [B&D&E]) | ([A - F] & [B&C&D&E]) + //[A,B,C - D] & [B,C,D - E] -> ([B,C - D - E]) | ([A - D] & [B,C - E]) + //[A,B,C - F] & [B,C,D - F] -> [B,C - F] | ([A - D - F]) | ([D - A - F]) + + } + }