001package conexp.fx.core.dl;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2019 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashSet;
029import java.util.Iterator;
030import java.util.List;
031import java.util.Map;
032import java.util.Map.Entry;
033import java.util.Set;
034import java.util.concurrent.ConcurrentHashMap;
035import java.util.function.BiFunction;
036import java.util.function.BiPredicate;
037import java.util.function.Function;
038import java.util.function.Supplier;
039import java.util.stream.Collectors;
040import java.util.stream.IntStream;
041import java.util.stream.Stream;
042
043import org.semanticweb.owlapi.apibinding.OWLManager;
044import org.semanticweb.owlapi.model.IRI;
045import org.semanticweb.owlapi.model.OWLClass;
046import org.semanticweb.owlapi.model.OWLClassExpression;
047import org.semanticweb.owlapi.model.OWLDataFactory;
048import org.semanticweb.owlapi.model.OWLObjectIntersectionOf;
049import org.semanticweb.owlapi.model.OWLObjectProperty;
050import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
051
052import com.google.common.collect.Collections2;
053import com.google.common.collect.HashMultimap;
054import com.google.common.collect.Multimap;
055import com.google.common.collect.Sets;
056
057import conexp.fx.core.collections.Collections3;
058import conexp.fx.core.collections.Pair;
059import conexp.fx.core.math.LatticeElement;
060import conexp.fx.core.util.UnicodeSymbols;
061
062public final class ELConceptDescription implements LatticeElement<ELConceptDescription>, Cloneable {
063
064  private static final OWLDataFactory df = OWLManager.getOWLDataFactory();
065
066  public static final ELConceptDescription of(final OWLClassExpression concept) {
067    return new ELConceptDescription(concept);
068  }
069
070  public static final ELConceptDescription parse(final String expression) {
071    return ELParser.read(expression);
072  }
073
074  public static final ELConceptDescription bot() {
075    return new ELConceptDescription(Sets.newHashSet(df.getOWLNothing().getIRI()), HashMultimap.create());
076  }
077
078  public static final ELConceptDescription top() {
079    return new ELConceptDescription();
080  }
081
082  public static final ELConceptDescription conceptName(final IRI conceptName) {
083    return new ELConceptDescription(Sets.newHashSet(conceptName), HashMultimap.create());
084  }
085
086  public static final ELConceptDescription
087      existentialRestriction(final IRI roleName, final ELConceptDescription filler) {
088    final ELConceptDescription C = new ELConceptDescription();
089    C.getExistentialRestrictions().put(roleName, filler);
090    return C;
091  }
092
093  public static final ELConceptDescription existentialRestriction(List<IRI> rs, ELConceptDescription C) {
094    ELConceptDescription rsC = C.clone();
095    for (IRI r : rs)
096      rsC = ELConceptDescription.existentialRestriction(r, rsC);
097    return rsC;
098  }
099
100  public static final ELConceptDescription existentialRestriction(final Entry<IRI, ELConceptDescription> entry) {
101    return entry.getValue().exists(entry.getKey());
102  }
103
104  public static final ELConceptDescription conjunction(final ELConceptDescription... conjuncts) {
105    return conjunction(Arrays.asList(conjuncts));
106  }
107
108  public static final ELConceptDescription conjunction(final Collection<ELConceptDescription> conjuncts) {
109    final ELConceptDescription conjunction = new ELConceptDescription();
110    conjuncts.forEach(conjunct -> {
111      conjunction.getConceptNames().addAll(conjunct.getConceptNames());
112      conjunction.getExistentialRestrictions().putAll(conjunct.getExistentialRestrictions());
113    });
114    return conjunction;
115  }
116
117  public static final ELConceptDescription
118      random(final Signature sigma, final int maxRoleDepth, final int minSize, final int maxSize) {
119    if (maxRoleDepth < 0 || minSize < 0 || maxSize < 0 || minSize > maxSize)
120      throw new IllegalArgumentException();
121    final double p = Math.random();
122    final double q = Math.random();
123    final ELConceptDescription C = new ELConceptDescription();
124    for (IRI A : sigma.getConceptNames())
125      if (C.size() >= maxSize)
126        return C;
127      else if (Math.random() < p)
128        C.conceptNames.add(A);
129    if (maxRoleDepth > 0)
130      for (IRI r : sigma.getRoleNames())
131        while (Math.random() < q)
132          if (C.size() >= maxSize)
133            return C;
134          else
135            C.existentialRestrictions
136                .put(r, ELConceptDescription.random(sigma, maxRoleDepth - 1, 0, maxSize - C.size()));
137    if (C.size() >= minSize)
138      return C;
139    else
140      return random(sigma, maxRoleDepth, minSize, maxSize);
141  }
142
143  public static final BiPredicate<ELConceptDescription, ELConceptDescription> quasiOrder() {
144    return (x, y) -> x.isSubsumedBy(y);
145  }
146
147  public static final BiPredicate<ELConceptDescription, ELConceptDescription> dualQuasiOrder() {
148    return (x, y) -> x.subsumes(y);
149  }
150
151  public static final BiPredicate<ELConceptDescription, ELConceptDescription> equivalence() {
152    return (x, y) -> x.isEquivalentTo(y);
153  }
154
155  public static final BiPredicate<ELConceptDescription, ELConceptDescription> neighborhood() {
156    return (x, y) -> x.isLowerNeighborOf(y);
157  }
158
159  public static final BiPredicate<ELConceptDescription, ELConceptDescription> dualNeighborhood() {
160    return (x, y) -> x.isUpperNeighborOf(y);
161  }
162
163  public static final BiFunction<ELConceptDescription, ELConceptDescription, Integer> distance() {
164    return (x, y) -> x.distanceTo(y);
165  }
166
167  private final Set<IRI>                            conceptNames;
168  private final Multimap<IRI, ELConceptDescription> existentialRestrictions;
169  // private final Set<Pair<IRI, ELConceptDescription>> valueRestrictions;
170  // private final Set<Pair<Pair<Integer, IRI>, ELConceptDescription>>
171  // qualifiedGreaterThanRestrictions;
172  // private final Set<Pair<Pair<Integer, IRI>, ELConceptDescription>>
173  // qualifiedSmallerThanRestrictions;
174  // private final Set<IRI> negatedConceptNames;
175  // private final Set<IRI> extistentialSelfRestrictions;
176
177  /**
178   * @param concept
179   * 
180   *          Creates a new EL normal form from an OWLClassExpression.
181   */
182  public ELConceptDescription(final OWLClassExpression concept) {
183    super();
184    this.conceptNames = new HashSet<>();
185    this.existentialRestrictions = HashMultimap.create();
186    if (concept.isOWLThing())
187      return;
188    if (concept.isOWLNothing()) {
189      conceptNames.add(df.getOWLNothing().getIRI());
190      return;
191    }
192    if (concept instanceof OWLClass) {
193      this.conceptNames.add(((OWLClass) concept).getIRI());
194      return;
195    }
196    if (concept instanceof OWLObjectSomeValuesFrom) {
197      final OWLObjectSomeValuesFrom existentialRestriction = (OWLObjectSomeValuesFrom) concept;
198      this.existentialRestrictions
199          .put(
200              ((OWLObjectProperty) existentialRestriction.getProperty()).getIRI(),
201              new ELConceptDescription(existentialRestriction.getFiller()));
202      return;
203    }
204    if (concept instanceof OWLObjectIntersectionOf) {
205      final OWLObjectIntersectionOf conjunction = (OWLObjectIntersectionOf) concept;
206      for (OWLClassExpression conjunct : conjunction.asConjunctSet())
207        if (conjunct instanceof OWLClass)
208          this.conceptNames.add(((OWLClass) conjunct).getIRI());
209        else if (conjunct instanceof OWLObjectSomeValuesFrom)
210          this.existentialRestrictions
211              .put(
212                  ((OWLObjectProperty) ((OWLObjectSomeValuesFrom) conjunct).getProperty()).getIRI(),
213                  new ELConceptDescription(((OWLObjectSomeValuesFrom) conjunct).getFiller()));
214        else
215          throw new ELSyntaxException();
216      return;
217    }
218    throw new ELSyntaxException();
219  }
220
221  /**
222   * @param conceptNames
223   * @param existentialRestrictions
224   * 
225   *          Creates a new EL normal form. If the sets conceptNames and existentitalRestrictions are both empty, then
226   *          the constructed normal form represents the top concept.
227   */
228  public ELConceptDescription(
229      final Set<IRI> conceptNames,
230      final Multimap<IRI, ELConceptDescription> existentialRestrictions) {
231    super();
232    this.conceptNames = conceptNames;
233    this.existentialRestrictions = existentialRestrictions;
234  }
235
236  public ELConceptDescription() {
237    this.conceptNames = Sets.newHashSet();
238    this.existentialRestrictions = HashMultimap.create();
239  }
240
241  public final boolean isBot() {
242    return conceptNames.contains(df.getOWLNothing().getIRI());
243  }
244
245  public final boolean isTop() {
246    return conceptNames.isEmpty() && existentialRestrictions.isEmpty();
247  }
248
249  public final Signature getSignature() {
250    final Signature sigma = new Signature(IRI.generateDocumentIRI());
251    sigma.getConceptNames().addAll(getConceptNamesInSignature().collect(Collectors.toSet()));
252    sigma.getRoleNames().addAll(getRoleNamesInSignature().collect(Collectors.toSet()));
253    return sigma;
254  }
255
256  protected final Stream<IRI> getConceptNamesInSignature() {
257    return Stream
258        .concat(
259            conceptNames.parallelStream(),
260            existentialRestrictions
261                .values()
262                .parallelStream()
263                .flatMap(ELConceptDescription::getConceptNamesInSignature));
264  }
265
266  protected final Stream<IRI> getRoleNamesInSignature() {
267    return Stream
268        .concat(
269            existentialRestrictions.keys().parallelStream(),
270            existentialRestrictions.values().parallelStream().flatMap(ELConceptDescription::getRoleNamesInSignature));
271  }
272
273  public final Set<IRI> getConceptNames() {
274    return conceptNames;
275  }
276
277  public final Multimap<IRI, ELConceptDescription> getExistentialRestrictions() {
278    return existentialRestrictions;
279  }
280
281  public final OWLClassExpression toOWLClassExpression() {
282    if (isTop())
283      return df.getOWLThing();
284    if (isBot())
285      return df.getOWLNothing();
286    if (conceptNames.size() == 1 && existentialRestrictions.isEmpty())
287      return df.getOWLClass(conceptNames.iterator().next());
288    if (conceptNames.isEmpty() && existentialRestrictions.size() == 1) {
289      final Entry<IRI, ELConceptDescription> existentialRestriction =
290          existentialRestrictions.entries().iterator().next();
291      return df
292          .getOWLObjectSomeValuesFrom(
293              df.getOWLObjectProperty(existentialRestriction.getKey()),
294              existentialRestriction.getValue().toOWLClassExpression());
295    }
296    final Set<OWLClassExpression> conjuncts = new HashSet<>();
297    for (IRI conceptName : conceptNames)
298      conjuncts.add(df.getOWLClass(conceptName));
299    for (Entry<IRI, ELConceptDescription> existentialRestriction : existentialRestrictions.entries())
300      conjuncts
301          .add(
302              df
303                  .getOWLObjectSomeValuesFrom(
304                      df.getOWLObjectProperty(existentialRestriction.getKey()),
305                      existentialRestriction.getValue().toOWLClassExpression()));
306    return df.getOWLObjectIntersectionOf(conjuncts);
307  }
308
309  public final ELConceptDescription and(final ELConceptDescription that) {
310    return ELConceptDescription.conjunction(this, that);
311  }
312
313  public final ELConceptDescription exists(final IRI roleName) {
314    return ELConceptDescription.existentialRestriction(roleName, this);
315  }
316
317  public final ELConceptDescription lcs(final ELConceptDescription that) {
318    return ELLeastCommonSubsumer.lcsOfMutuallyIncomparable(this, that);
319  }
320
321  public final ELConceptDescription without(final ELConceptDescription that) {
322    final ELConceptDescription result = this.clone();
323    result.getConceptNames().removeAll(that.getConceptNames());
324    result
325        .getExistentialRestrictions()
326        .entries()
327        .removeIf(rE -> that.isSubsumedBy(ELConceptDescription.existentialRestriction(rE)));
328    return result;
329  }
330
331  public final boolean isSubsumedBy(final ELConceptDescription other) {
332    return ELReasoner.isSubsumedBy(this, other);
333  }
334
335  public final boolean subsumes(final ELConceptDescription other) {
336    return ELReasoner.subsumes(this, other);
337  }
338
339  public final boolean isEquivalentTo(final ELConceptDescription other) {
340    return Stream
341        .<Supplier<Boolean>> of(() -> this.subsumes(other), () -> other.subsumes(this))
342        .parallel()
343        .allMatch(Supplier::get);
344  }
345
346  public final boolean isLowerNeighborOf(final ELConceptDescription other) {
347    return this.upperNeighborsReduced().parallelStream().anyMatch(other::isEquivalentTo);
348  }
349
350  public final boolean isUpperNeighborOf(final ELConceptDescription other) {
351    return other.isLowerNeighborOf(this);
352  }
353
354  public final ELConceptDescription reduce() {
355    for (Entry<IRI, ELConceptDescription> er : existentialRestrictions.entries())
356      er.getValue().reduce();
357    final Function<Entry<IRI, ELConceptDescription>, Stream<Pair<Entry<IRI, ELConceptDescription>, Entry<IRI, ELConceptDescription>>>> f =
358        er1 -> existentialRestrictions
359            .entries()
360            .parallelStream()
361            .filter(er2 -> er1.getKey().equals(er2.getKey()))
362            .filter(er2 -> !er1.equals(er2))
363            .map(er2 -> Pair.of(er1, er2));
364    final Set<Entry<IRI, ELConceptDescription>> superfluousERs = existentialRestrictions
365        .entries()
366        .parallelStream()
367        .map(f)
368        .reduce(Stream::concat)
369        .orElseGet(Stream::empty)
370        .filter(p -> p.first().getValue().isSubsumedBy(p.second().getValue()))
371        .map(Pair::second)
372        .collect(Collectors.toSet());
373    superfluousERs.forEach(x -> existentialRestrictions.remove(x.getKey(), x.getValue()));
374    return this;
375  }
376
377  public final int roleDepth() {
378    if (existentialRestrictions.isEmpty())
379      return 0;
380    return 1 + existentialRestrictions
381        .entries()
382        .parallelStream()
383        .map(Entry::getValue)
384        .map(ELConceptDescription::roleDepth)
385        .max(Integer::compare)
386        .orElse(0);
387  }
388
389  public final void restrictTo(final int roleDepth) {
390    if (roleDepth < 0)
391      throw new IllegalArgumentException();
392    else if (roleDepth == 0)
393      existentialRestrictions.clear();
394    else
395      existentialRestrictions.values().parallelStream().forEach(filler -> filler.restrictTo(roleDepth - 1));
396  }
397
398  public final Collection<ELConceptDescription> topLevelConjuncts() {
399    return Collections3
400        .union(
401            Collections2.transform(conceptNames, ELConceptDescription::conceptName),
402            Collections2.transform(existentialRestrictions.entries(), ELConceptDescription::existentialRestriction));
403//    return Collections3.transform(
404//        conceptNames,
405//        GuavaIsomorphism.create(A -> ELConceptDescription.conceptName(A), A -> A.getConceptNames().iterator().next()));
406//    return conceptNames.size() + existentialRestrictions.size();
407  }
408
409  public final int size() {
410    return Math
411        .max(
412            1,
413            2 * conceptNames.size() + existentialRestrictions.size() - 1
414                + existentialRestrictions
415                    .entries()
416                    .parallelStream()
417                    .map(Entry::getValue)
418                    .map((ELConceptDescription c) -> 1 + c.size())
419                    .reduce(0, Integer::sum));
420  }
421
422  public final int size2() {
423    return conceptNames.size() + existentialRestrictions
424        .entries()
425        .parallelStream()
426        .map(Entry::getValue)
427        .map(C -> 1 + C.size2())
428        .reduce(0, Integer::sum);
429  }
430
431  public final long rank5() {
432    long rank = 0;
433    ELConceptDescription C = this.clone().reduce();
434    while (!C.isTop()) {
435      C = C.oneUpperNeighbor();
436      rank++;
437    }
438    return rank;
439  }
440
441  public final int rank() {
442    return this.clone().reduce().unreducedRank();
443  }
444
445  public final int unreducedRank() {
446    if (this.isTop())
447      return 0;
448    else
449      return 1 + this.oneUpperNeighbor().unreducedRank();
450  }
451
452  private final ELConceptDescription oneUpperNeighbor() {
453    if (!this.conceptNames.isEmpty()) {
454      final ELConceptDescription upperNeighbor = this.clone();
455      final Iterator<IRI> it = upperNeighbor.conceptNames.iterator();
456      it.next();
457      it.remove();
458      return upperNeighbor;
459    } else if (!this.existentialRestrictions.isEmpty()) {
460      final ELConceptDescription upperNeighbor = this.clone();
461      final Iterator<Entry<IRI, ELConceptDescription>> it = upperNeighbor.existentialRestrictions.entries().iterator();
462      final Entry<IRI, ELConceptDescription> ER = it.next();
463      it.remove();
464      ER
465          .getValue()
466          .upperNeighborsReduced()
467          .parallelStream()
468          .filter(
469              uER -> upperNeighbor.existentialRestrictions
470                  .entries()
471                  .parallelStream()
472                  .filter(otherER -> !otherER.equals(ER))
473                  .filter(otherER -> ER.getKey().equals(otherER.getKey()))
474                  .map(Entry::getValue)
475                  .noneMatch(uER::subsumes))
476          .map(uER -> Pair.of(ER.getKey(), uER))
477          .sequential()
478          .forEach(p -> upperNeighbor.existentialRestrictions.put(p.first(), p.second()));
479      return upperNeighbor;
480    } else
481      return null;
482  }
483
484//  public final int rank2() {
485//    final ELConceptDescription C = this.clone().reduce();
486//    int r = C.conceptNames.size();
487//    C.conceptNames.clear();
488//    if (C.existentialRestrictions.isEmpty())
489//      return r;
490//    else if (C.existentialRestrictions.size() == 1)
491//      return r + rank2existentialRestriction(
492//          ELConceptDescription.existentialRestriction(C.existentialRestrictions.iterator().next()));
493//    else
494//      return r + rank2conjunction(C);
495//  }
496//
497//  private static final int rank2conjunction(final ELConceptDescription conjunction) {
498//    if (!conjunction.conceptNames.isEmpty())
499//      throw new IllegalArgumentException();
500//    final Set<Pair<IRI, ELConceptDescription>> conjuncts = conjunction.existentialRestrictions;
501//    if (conjuncts.isEmpty())
502//      return 0;
503//    else if (conjuncts.size() == 1)
504//      return rank2existentialRestriction(ELConceptDescription.existentialRestriction(conjuncts.iterator().next()));
505//    else {
506//      return IntStream.range(1, conjuncts.size() + 1).boxed().map(i -> {
507//        return (int) (Math.pow(-1, i + 1) * Sets
508//            .combinations(
509//                conjuncts
510//                    .parallelStream()
511//                    .map(ELConceptDescription::existentialRestriction)
512//                    // .map(ELConceptDescription::clone)
513//                    .collect(Collectors.toSet()),
514//                i)
515//            .parallelStream()
516//            .map(ELLeastCommonSubsumer::_of)
517//            .map(ELConceptDescription::clone)
518//            // .map(ELConceptDescription::getReducedForm)
519//            .collect(Collectors.summingInt(ELConceptDescription::rank2conjunction)));
520//      }).collect(Collectors.summingInt(Integer::intValue));
521//    }
522//  }
523//
524//  private static final int rank2existentialRestriction(final ELConceptDescription existentialRestriction) {
525//    if (!existentialRestriction.conceptNames.isEmpty())
526//      throw new IllegalArgumentException();
527//    else if (existentialRestriction.existentialRestrictions.size() != 1)
528//      throw new IllegalArgumentException();
529//    else {
530//      final Pair<IRI, ELConceptDescription> rC = existentialRestriction.existentialRestrictions.iterator().next();
531//      final IRI r = rC.first();
532//      final ELConceptDescription C = rC.second();
533//      C.reduce();
534//      return 1 + rank2conjunction(
535//          new ELConceptDescription(
536//              new HashSet<>(),
537//              C.upperNeighborsReduced().parallelStream().map(D -> Pair.of(r, D)).collect(Collectors.toSet())));
538//    }
539//  }
540
541  public final int rank2() {
542    return this.clone().reduce().unreducedRank2();
543  }
544
545  public final int unreducedRank2() {
546    return this.getConceptNames().size() + this.getExistentialRestrictions().keySet().parallelStream().map(r -> {
547      final ELConceptDescription rDs = new ELConceptDescription();
548      rDs.getExistentialRestrictions().putAll(r, this.getExistentialRestrictions().get(r));
549      return rDs;
550    }).mapToInt(ELConceptDescription::unreducedRank).sum();
551  }
552
553  public final int rank3() {
554//    final Set<ELConceptDescription> Us = representatives(upperNeighborsReduced(), (X, Y) -> X.isEquivalentTo(Y));
555    final Set<ELConceptDescription> Us = upperNeighborsReduced();
556//    if (Us.parallelStream().anyMatch(X -> Us.parallelStream().filter(Y -> X != Y).anyMatch(X::isEquivalentTo)))
557//      throw new RuntimeException();
558    if (Us.isEmpty())
559      return 0;
560    else if (Us.size() == 1)
561      return 1 + Us.iterator().next().clone().reduce().rank3();
562    else
563      return Us.size() + ELLeastCommonSubsumer.lcs(Us).clone().reduce().rank3();
564  }
565
566  public final int rank4() {
567    return this.clone().reduce().unreducedRank4();
568  }
569
570  public final int unreducedRank4() {
571    return this.getConceptNames().size() + this
572        .getExistentialRestrictions()
573        .keySet()
574        .parallelStream()
575        .map(r -> this.getExistentialRestrictions().get(r))
576        .mapToInt(ELConceptDescription::recurseUnreducedRank4)
577        .sum();
578  }
579
580  private static final int recurseUnreducedRank4(final Collection<ELConceptDescription> Ds) {
581    if (Ds.isEmpty())
582      return 0;
583    else if (Ds.size() == 1)
584      return 1 + recurseUnreducedRank4(Ds.iterator().next().upperNeighborsReduced());
585    else
586      return IntStream
587          .range(1, Ds.size() + 1)
588          .boxed()
589          .map(
590              i -> (int) (Math.pow(-1, i + 1) * Sets
591                  .combinations(Sets.newHashSet(Ds), i)
592                  .parallelStream()
593                  .map(ELLeastCommonSubsumer::lcs)
594                  .map(ELConceptDescription::reduce)
595                  .collect(Collectors.toSet())
596                  .parallelStream()
597                  .map(Collections::singleton)
598                  .collect(Collectors.summingInt(ELConceptDescription::recurseUnreducedRank4))))
599          .collect(Collectors.summingInt(Integer::valueOf));
600  }
601
602  public final int distanceTo(final ELConceptDescription other) {
603    final List<Integer> x = Stream
604        .<Supplier<Integer>> of(
605            () -> ELConceptDescription.conjunction(this, other).rank(),
606            () -> ELLeastCommonSubsumer.lcs(this, other).rank())
607        .parallel()
608        .map(Supplier::get)
609        .collect(Collectors.toList());
610    return x.get(0) - x.get(1);
611  }
612
613  public final int distanceTo2(final ELConceptDescription other) {
614    final ELConceptDescription lcs = ELLeastCommonSubsumer.lcs(this, other);
615    int distance = -1;
616    for (ELConceptDescription C = ELConceptDescription.conjunction(this, other).reduce(); C != null; C =
617        C.oneUpperNeighborBelow(lcs))
618      distance++;
619    return distance;
620  }
621
622  private final ELConceptDescription oneUpperNeighborBelow(final ELConceptDescription other) {
623    return Stream.concat(this.conceptNames.parallelStream().map(A -> {
624      final ELConceptDescription upperNeighbor = this.clone();
625      upperNeighbor.conceptNames.remove(A);
626      return upperNeighbor;
627    }), this.existentialRestrictions.entries().parallelStream().map(ER -> {
628      final ELConceptDescription upperNeighbor = this.clone();
629      upperNeighbor.existentialRestrictions.remove(ER.getKey(), ER.getValue());
630      ER
631          .getValue()
632          .upperNeighborsReduced()
633          .parallelStream()
634          .filter(
635              uER -> upperNeighbor.existentialRestrictions
636                  .entries()
637                  .parallelStream()
638                  .filter(otherER -> !otherER.equals(ER))
639                  .filter(otherER -> ER.getKey().equals(otherER.getKey()))
640                  .map(Entry::getValue)
641                  .noneMatch(uER::subsumes))
642          .map(uER -> Pair.of(ER.getKey(), uER))
643          .sequential()
644          .forEach(p -> upperNeighbor.existentialRestrictions.put(p.first(), p.second()));
645      return upperNeighbor;
646    })).filter(other::subsumes).findAny().orElse(null);
647  }
648
649  public final Set<ELConceptDescription> neighborhood(final int radius, final Signature sigma) {
650    if (radius < 0)
651      throw new IllegalArgumentException();
652    final Set<ELConceptDescription> next = Sets.newHashSet(this);
653    final Set<ELConceptDescription> neighborhood = Sets.newHashSet(this);
654    for (int k = 0; k < radius; k++) {
655      final Set<ELConceptDescription> news = Stream
656          .concat(
657              next.parallelStream().flatMap(C -> C.upperNeighborsReduced().parallelStream()),
658              next.parallelStream().flatMap(C -> C.lowerNeighborsReduced1(sigma).parallelStream()))
659          .collect(Collectors.toSet());
660      next.clear();
661      next.addAll(news);
662      next.removeAll(neighborhood);
663      neighborhood.addAll(news);
664    }
665    return neighborhood;
666  }
667
668  public final Set<ELConceptDescription> upperNeighbors() {
669    final ELConceptDescription reducedForm = this.clone().reduce();
670    return Stream.concat(reducedForm.conceptNames.parallelStream().map(A -> {
671      final ELConceptDescription upperNeighbor = reducedForm.clone();
672      upperNeighbor.conceptNames.remove(A);
673      return upperNeighbor;
674    }), reducedForm.existentialRestrictions.entries().parallelStream().map(ER -> {
675      final ELConceptDescription upperNeighbor = reducedForm.clone();
676      upperNeighbor.existentialRestrictions.remove(ER.getKey(), ER.getValue());
677      ER.getValue().upperNeighbors().forEach(uER -> upperNeighbor.existentialRestrictions.put(ER.getKey(), uER));
678      return upperNeighbor;
679    })
680//        .filter(other -> !this.subsumes(other)))
681    ).collect(Collectors.toSet());
682  }
683
684  public final Set<ELConceptDescription> upperNeighborsReduced() {
685    final ELConceptDescription reducedForm = this.clone().reduce();
686    return Collections3.representatives(Stream.concat(reducedForm.conceptNames.parallelStream().map(A -> {
687      final ELConceptDescription upperNeighbor = reducedForm.clone();
688      upperNeighbor.conceptNames.remove(A);
689      return upperNeighbor;
690    }), reducedForm.existentialRestrictions.entries().parallelStream().map(ER -> {
691      final ELConceptDescription upperNeighbor = reducedForm.clone();
692      upperNeighbor.existentialRestrictions.remove(ER.getKey(), ER.getValue());
693      ER
694          .getValue()
695          .upperNeighborsReduced()
696          .parallelStream()
697          .filter(
698              uER -> reducedForm.existentialRestrictions
699                  .entries()
700                  .parallelStream()
701                  .filter(otherER -> !otherER.equals(ER))
702                  .filter(otherER -> ER.getKey().equals(otherER.getKey()))
703                  .map(Entry::getValue)
704                  .noneMatch(uER::subsumes))
705          .sequential()
706          .forEach(uER -> upperNeighbor.existentialRestrictions.put(ER.getKey(), uER));
707      return upperNeighbor;
708    })
709//        .filter(other -> !this.subsumes(other)))
710    ).collect(Collectors.toSet()), (X, Y) -> X.isEquivalentTo(Y));
711  }
712
713  public final Set<ELConceptDescription> lowerNeighbors(final Signature sigma) {
714    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
715    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
716    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
717      final ELConceptDescription lowerNeighbor = C.clone();
718      lowerNeighbor.conceptNames.add(A);
719      return lowerNeighbor;
720    }).forEach(lowerNeighbors::add);
721    sigma.getRoleNames().parallelStream().forEach(r -> {
722      final Set<ELConceptDescription> filter = C.existentialRestrictions
723          .entries()
724          .parallelStream()
725          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
726          .map(Entry::getValue)
727          .collect(Collectors.toSet());
728//      if (filter.isEmpty()) {
729//        final ELConceptDescription lowerNeighbor = C.clone();
730//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
731//        lowerNeighbors.add(lowerNeighbor);
732//      } else {
733      Sets
734          .powerSet(filter)
735          .parallelStream()
736          // .filter(FF -> !FF.isEmpty())
737          .forEach(FF -> {
738            final Map<ELConceptDescription, Set<ELConceptDescription>> choices = new ConcurrentHashMap<>();
739            FF.parallelStream().forEach(F -> {
740              final Set<ELConceptDescription> choicesForF = Sets.newConcurrentHashSet();
741              choices.put(F, choicesForF);
742              F.lowerNeighbors(sigma).parallelStream().forEach(L -> {
743                // final ELConceptDescription X = L.clone();
744                // X.getConceptNames().removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
745                // X.getExistentialRestrictions().entries().removeIf(
746                // rD -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rD)));
747                final ELConceptDescription X = L.without(F);
748                if (X.isTop()) {
749                  System.out.println("F = " + F);
750                  System.out.println("L = " + L);
751                  System.out.println("X = " + X);
752                  throw new RuntimeException();
753                }
754                if (FF.parallelStream().filter(F1 -> !F.equals(F1)).allMatch(F1 -> F1.isSubsumedBy(X)))
755                  choicesForF.add(X);
756              });
757            });
758            choices
759                .keySet()
760                .parallelStream()
761                .map(choices::get)
762                .reduce(
763                    Collections.singleton(Collections.<ELConceptDescription> emptySet()),
764                    (X, Y) -> X.parallelStream().flatMap(x -> Y.parallelStream().map(y -> {
765                      final Set<ELConceptDescription> xy = Sets.newHashSet(x);
766                      xy.add(y);
767                      return xy;
768                    })).collect(Collectors.toSet()),
769                    (X, Y) -> X.parallelStream().flatMap(x -> Y.parallelStream().map(y -> {
770                      final Set<ELConceptDescription> xy = Sets.newHashSet(x);
771                      xy.addAll(y);
772                      return xy;
773                    })).collect(Collectors.toSet()))
774                .parallelStream()
775                .forEach(f -> {
776                  final ELConceptDescription D = ELConceptDescription.conjunction(f);
777                  if (filter.parallelStream().filter(F0 -> !FF.contains(F0)).noneMatch(F0 -> F0.isSubsumedBy(D))) {
778                    final ELConceptDescription C_and_rD = C.clone();
779                    C_and_rD.getExistentialRestrictions().put(r, D.clone());
780                    lowerNeighbors.add(C_and_rD);
781                    // System.out.println("new lower neighbor found: C⊓∃" + r + "." + D);
782                  }
783                });
784          });
785//      }
786    });
787    return lowerNeighbors;
788  }
789
790  public final Set<ELConceptDescription> lowerNeighborsA(final Signature sigma) {
791    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
792    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
793    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
794      final ELConceptDescription lowerNeighbor = C.clone();
795      lowerNeighbor.conceptNames.add(A);
796      return lowerNeighbor;
797    }).forEach(lowerNeighbors::add);
798    sigma.getRoleNames().parallelStream().forEach(r -> {
799      final Set<ELConceptDescription> filter = C.existentialRestrictions
800          .entries()
801          .parallelStream()
802          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
803          .map(Entry::getValue)
804          .collect(Collectors.toSet());
805//      if (filter.isEmpty()) {
806//        final ELConceptDescription lowerNeighbor = C.clone();
807//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
808//        lowerNeighbors.add(lowerNeighbor);
809//      } else {
810      final Map<ELConceptDescription, Set<ELConceptDescription>> choices = new ConcurrentHashMap<>();
811      filter.parallelStream().forEach(F -> {
812        final Set<ELConceptDescription> choicesForF = Sets.newConcurrentHashSet();
813        choices.put(F, choicesForF);
814        F.lowerNeighbors(sigma).parallelStream().forEach(L -> {
815          choicesForF.add(L.without(F));
816        });
817      });
818      Sets
819          .powerSet(filter)
820          .parallelStream()
821          // .filter(FF -> !FF.isEmpty())
822          .forEach(FF -> {
823            choices
824                .entrySet()
825                .parallelStream()
826                .filter(entry -> FF.contains(entry.getKey()))
827                .map(
828                    entry -> Sets
829                        .filter(
830                            entry.getValue(),
831                            X -> FF
832                                .parallelStream()
833                                .filter(F1 -> !entry.getKey().equals(F1))
834                                .allMatch(F1 -> F1.isSubsumedBy(X))))
835                .reduce(
836                    Collections.singleton(Collections.<ELConceptDescription> emptySet()),
837                    (X, Y) -> X.parallelStream().flatMap(x -> Y.parallelStream().map(y -> {
838                      final Set<ELConceptDescription> xy = Sets.newHashSet(x);
839                      xy.add(y);
840                      return xy;
841                    })).collect(Collectors.toSet()),
842                    (X, Y) -> X.parallelStream().flatMap(x -> Y.parallelStream().map(y -> {
843                      final Set<ELConceptDescription> xy = Sets.newHashSet(x);
844                      xy.addAll(y);
845                      return xy;
846                    })).collect(Collectors.toSet()))
847                .parallelStream()
848                .forEach(f -> {
849                  final ELConceptDescription D = ELConceptDescription.conjunction(f);
850                  if (filter.parallelStream().filter(F0 -> !FF.contains(F0)).noneMatch(F0 -> F0.isSubsumedBy(D))) {
851                    final ELConceptDescription C_and_rD = C.clone();
852                    C_and_rD.getExistentialRestrictions().put(r, D.clone());
853                    lowerNeighbors.add(C_and_rD);
854                    // System.out.println("new lower neighbor found: C⊓∃" + r + "." + D);
855                  }
856                });
857          });
858//      }
859    });
860    return lowerNeighbors;
861  }
862
863  public final Set<ELConceptDescription> lowerNeighborsB(final Signature sigma) {
864    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
865    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
866    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
867      final ELConceptDescription lowerNeighbor = C.clone();
868      lowerNeighbor.conceptNames.add(A);
869      return lowerNeighbor;
870    }).forEach(lowerNeighbors::add);
871    sigma.getRoleNames().parallelStream().forEach(r -> {
872      final Set<ELConceptDescription> filter = C.existentialRestrictions
873          .entries()
874          .parallelStream()
875          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
876          .map(Entry::getValue)
877          .collect(Collectors.toSet());
878//      if (filter.isEmpty()) {
879//        final ELConceptDescription lowerNeighbor = C.clone();
880//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
881//        lowerNeighbors.add(lowerNeighbor);
882//      } else {
883      final Map<ELConceptDescription, Set<ELConceptDescription>> choices = new ConcurrentHashMap<>();
884      filter.parallelStream().forEach(F -> {
885        final Set<ELConceptDescription> choicesForF = Sets.newConcurrentHashSet();
886        choices.put(F, choicesForF);
887        F.lowerNeighbors(sigma).parallelStream().forEach(L -> {
888          choicesForF.add(L.without(F));
889        });
890      });
891      final Map<ELConceptDescription, Set<ELConceptDescription>> subsumees = new ConcurrentHashMap<>();
892      choices.entrySet().parallelStream().forEach(entry -> {
893//          final ELConceptDescription F = entry.getKey();
894        final Set<ELConceptDescription> choicesForF = entry.getValue();
895        choicesForF.parallelStream().forEach(X -> {
896          final Set<ELConceptDescription> subsumeesOfX = Sets.newConcurrentHashSet();
897          subsumees.put(X, subsumeesOfX);
898          filter.parallelStream().filter(X::subsumes).forEach(subsumeesOfX::add);
899        });
900      });
901      Sets
902          .powerSet(filter)
903          .parallelStream()
904          // .filter(FF -> !FF.isEmpty())
905          .forEach(FF -> {
906            choices
907                .entrySet()
908                .parallelStream()
909                .filter(entry -> FF.contains(entry.getKey()))
910                .map(
911                    entry -> Sets
912                        .filter(
913                            entry.getValue(),
914                            X -> FF
915                                .parallelStream()
916                                .filter(F1 -> !entry.getKey().equals(F1))
917                                .allMatch(F1 -> subsumees.get(X).contains(F1))))
918                .reduce(
919                    Collections.singleton(Collections.<ELConceptDescription> emptySet()),
920                    (X, Y) -> X.parallelStream().flatMap(x -> Y.parallelStream().map(y -> {
921                      final Set<ELConceptDescription> xy = Sets.newHashSet(x);
922                      xy.add(y);
923                      return xy;
924                    })).collect(Collectors.toSet()),
925                    (X, Y) -> X.parallelStream().flatMap(x -> Y.parallelStream().map(y -> {
926                      final Set<ELConceptDescription> xy = Sets.newHashSet(x);
927                      xy.addAll(y);
928                      return xy;
929                    })).collect(Collectors.toSet()))
930                .parallelStream()
931                .forEach(f -> {
932                  final ELConceptDescription D = ELConceptDescription.conjunction(f);
933                  if (filter
934                      .parallelStream()
935                      .filter(F0 -> !FF.contains(F0))
936                      .noneMatch(F0 -> f.parallelStream().allMatch(X -> subsumees.get(X).contains(F0)))) {
937                    final ELConceptDescription C_and_rD = C.clone();
938                    C_and_rD.getExistentialRestrictions().put(r, D.clone());
939                    lowerNeighbors.add(C_and_rD);
940                    // System.out.println("new lower neighbor found: C⊓∃" + r + "." + D);
941                  }
942                });
943          });
944//      }
945    });
946    return lowerNeighbors;
947  }
948
949  public final Set<ELConceptDescription> lowerNeighbors1(final Signature sigma) {
950    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
951    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
952    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
953      final ELConceptDescription lowerNeighbor = C.clone();
954      lowerNeighbor.conceptNames.add(A);
955      return lowerNeighbor;
956    }).forEach(lowerNeighbors::add);
957    sigma.getRoleNames().parallelStream().forEach(r -> {
958      final Set<ELConceptDescription> filter = C.existentialRestrictions
959          .entries()
960          .parallelStream()
961          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
962          .map(Entry::getValue)
963          .collect(Collectors.toSet());
964      if (filter.isEmpty()) {
965        final ELConceptDescription lowerNeighbor = C.clone();
966        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
967        lowerNeighbors.add(lowerNeighbor);
968      } else
969        recurseLowerNeighbors1(sigma, r, C, Collections.singleton(ELConceptDescription.top()), filter, lowerNeighbors);
970    });
971    return lowerNeighbors;
972  }
973
974  private final void recurseLowerNeighbors1(
975      final Signature sigma,
976      final IRI r,
977      final ELConceptDescription C,
978      final Set<ELConceptDescription> currentCandidates,
979      final Set<ELConceptDescription> filter,
980      final Set<ELConceptDescription> lowerNeighbors) {
981    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
982    currentCandidates.parallelStream().forEach(D -> {
983      if (!D
984          .upperNeighborsReduced()
985          .parallelStream()
986          .allMatch(U -> C.isSubsumedBy(ELConceptDescription.existentialRestriction(r, U))))
987        return;
988      else if (filter.parallelStream().anyMatch(F -> F.isSubsumedBy(D)))
989        D.lowerNeighbors1(sigma).parallelStream().forEach(nextCandidates::add);
990      else {
991        final ELConceptDescription X = C.clone();
992        X.existentialRestrictions.put(r, D);
993        lowerNeighbors.add(X);
994      }
995    });
996    if (!nextCandidates.isEmpty())
997      recurseLowerNeighbors1(sigma, r, C, nextCandidates, filter, lowerNeighbors);
998  }
999
1000  public final Set<ELConceptDescription> lowerNeighborsReduced1(final Signature sigma) {
1001//    final Set<ELConceptDescription> lowerNeighbors = lowerNeighbors(sigma);
1002//    lowerNeighbors.parallelStream().forEach(ELConceptDescription::reduce);
1003    final Set<ELConceptDescription> lowerNeighbors = new HashSet<>();
1004    for (ELConceptDescription lowerNeighbor : lowerNeighbors1(sigma))
1005      lowerNeighbors.add(lowerNeighbor.reduce());
1006    return lowerNeighbors;
1007  }
1008
1009//  protected final Set<ELConceptDescription> lowerNeighbors2(final Signature sigma) {
1010//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1011//    sigma.getConceptNames().parallelStream().filter(A -> !ELConceptDescription.this.conceptNames.contains(A)).map(A -> {
1012//      final ELConceptDescription lowerNeighbor = ELConceptDescription.this.clone();
1013//      lowerNeighbor.conceptNames.add(A);
1014//      return lowerNeighbor;
1015//    }).forEach(lowerNeighbors::add);
1016//    sigma.getRoleNames().parallelStream().forEach(r -> {
1017//      final ELConceptDescription rLCS = ELLeastCommonSubsumer.lcs(
1018//          ELConceptDescription.this.clone().reduce().existentialRestrictions
1019//              .entries()
1020//              .parallelStream()
1021//              .filter(sD -> sD.getKey().equals(r))
1022//              .map(Entry::getValue)
1023//              .collect(Collectors.toSet()));
1024//      rLCS.reduce();
1025////      recurseLowerNeighbors(sigma, r, new ELConceptDescription(), lowerNeighbors);
1026//      recurseLowerNeighbors2a(sigma, r, rLCS, lowerNeighbors);
1027//      recurseLowerNeighbors2b(sigma, r, new ELConceptDescription(), rLCS, lowerNeighbors);
1028//    });
1029//    return lowerNeighbors;
1030//  }
1031//
1032//  private final void recurseLowerNeighbors2a(
1033//      final Signature sigma,
1034//      final IRI r,
1035//      final ELConceptDescription D,
1036//      final Set<ELConceptDescription> lowerNeighbors) {
1037//    if (ELConceptDescription.this.isSubsumedBy(ELConceptDescription.existentialRestriction(r, D)))
1038//      D.lowerNeighbors2(sigma).parallelStream().forEach(E -> recurseLowerNeighbors2a(sigma, r, E, lowerNeighbors));
1039//    else if (D.upperNeighborsReduced().parallelStream().allMatch(
1040//        E -> ELConceptDescription.this.isSubsumedBy(ELConceptDescription.existentialRestriction(r, E)))) {
1041//      final ELConceptDescription lowerNeighbor = ELConceptDescription.this.clone();
1042//      lowerNeighbor.existentialRestrictions.put(r, D);
1043//      lowerNeighbors.add(lowerNeighbor);
1044//    }
1045//  }
1046//
1047//  private final void recurseLowerNeighbors2b(
1048//      final Signature sigma,
1049//      final IRI r,
1050//      final ELConceptDescription D,
1051//      final ELConceptDescription rLCS,
1052//      final Set<ELConceptDescription> lowerNeighbors) {
1053//    if (rLCS.subsumes(D))
1054//      return;
1055//    else if (ELConceptDescription.this.isSubsumedBy(ELConceptDescription.existentialRestriction(r, D)))
1056//      D.lowerNeighbors2(sigma).parallelStream().forEach(
1057//          E -> recurseLowerNeighbors2b(sigma, r, E, rLCS, lowerNeighbors));
1058//    else if (D.upperNeighborsReduced().parallelStream().allMatch(
1059//        E -> ELConceptDescription.this.isSubsumedBy(ELConceptDescription.existentialRestriction(r, E)))) {
1060//      final ELConceptDescription lowerNeighbor = ELConceptDescription.this.clone();
1061//      lowerNeighbor.existentialRestrictions.put(r, D);
1062//      lowerNeighbors.add(lowerNeighbor);
1063//    }
1064//  }
1065//
1066//  protected final Set<ELConceptDescription> lowerNeighbors3(final Signature sigma) {
1067//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1068//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1069//    sigma.getConceptNames().parallelStream().filter(A -> !ELConceptDescription.this.conceptNames.contains(A)).map(A -> {
1070//      final ELConceptDescription lowerNeighbor = C.clone();
1071//      lowerNeighbor.conceptNames.add(A);
1072//      return lowerNeighbor;
1073//    }).forEach(lowerNeighbors::add);
1074//    sigma.getRoleNames().parallelStream().forEach(r -> {
1075//      final Set<ELConceptDescription> Ds = C.existentialRestrictions
1076//          .entries()
1077//          .parallelStream()
1078//          .filter(sD -> sD.getKey().equals(r))
1079//          .map(Entry::getValue)
1080//          .collect(Collectors.toSet());
1081//      if (Ds.isEmpty()) {
1082//        final ELConceptDescription X = C.clone();
1083//        X.existentialRestrictions.put(r, ELConceptDescription.top());
1084//        lowerNeighbors.add(X);
1085//      } else
1086////        Stream
1087////            .concat(
1088//        // Collections.<Pair<Set<ELConceptDescription>, ELConceptDescription>> emptySet().parallelStream(),
1089//        Collections
1090//            .singleton(Pair.of(Ds, ELConceptDescription.top()))
1091//            .parallelStream()
1092//            // ,
1093//            // Sets.powerSet(Ds).parallelStream().filter(Es -> Es.size() > 0).map(Es -> {
1094//            // final ELConceptDescription lcsEs = ELLeastCommonSubsumer._of(Es);
1095//            // lcsEs.reduce();
1096//            // return Pair.of(Es, lcsEs);
1097//            // }))
1098//            .forEach(p -> {
1099//              recurseLowerNeighbors3(sigma, r, C, p.second(), p.first(), lowerNeighbors);
1100//              // p.second().lowerNeighbors3(sigma).parallelStream().forEach(
1101//              // E -> recurseLowerNeighbors3(sigma, r, C, E, p.first(), lowerNeighbors));
1102//            });
1103//    });
1104//    return lowerNeighbors;
1105//  }
1106//
1107//  private final void recurseLowerNeighbors3(
1108//      final Signature sigma,
1109//      final IRI r,
1110//      final ELConceptDescription C,
1111//      final ELConceptDescription D,
1112//      final Set<ELConceptDescription> filter,
1113//      final Set<ELConceptDescription> lowerNeighbors) {
1114//    if (!D.upperNeighborsReduced().parallelStream().allMatch(
1115//        U -> C.isSubsumedBy(ELConceptDescription.existentialRestriction(r, U))))
1116//      return;
1117//    else if (filter.parallelStream().anyMatch(F -> F.isSubsumedBy(D)))
1118//      D.lowerNeighbors3(sigma).parallelStream().forEach(
1119//          E -> recurseLowerNeighbors3(sigma, r, C, E, filter, lowerNeighbors));
1120////      return;
1121//    else {
1122//      final ELConceptDescription X = C.clone();
1123//      X.existentialRestrictions.put(r, D);
1124//      lowerNeighbors.add(X);
1125//    }
1126////    D
1127////        .lowerNeighbors3(sigma)
1128////        .parallelStream()
1129////        .filter(L -> filter.parallelStream().allMatch(E -> !E.isSubsumedBy(L)))
1130////        .filter(
1131////            L -> L.upperNeighborsReduced().parallelStream().allMatch(
1132////                U -> C.isSubsumedBy(ELConceptDescription.existentialRestriction(Pair.of(r, U)))))
1133////        .map(L -> {
1134////          final ELConceptDescription X = C.clone();
1135////          X.existentialRestrictions.add(Pair.of(r, L));
1136////          return X;
1137////        })
1138////        .forEach(lowerNeighbors::add);
1139//  }
1140//
1141////  public final Set<ELConceptDescription> lowerNeighbors2(final Signature sigma) {
1142////    this.reduce();
1143////    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1144////    sigma.getConceptNames().parallelStream().filter(A -> !ELConceptDescription.this.conceptNames.contains(A)).map(A -> {
1145////      final ELConceptDescription lowerNeighbor = ELConceptDescription.this.clone();
1146////      lowerNeighbor.conceptNames.add(A);
1147////      return lowerNeighbor;
1148////    }).forEach(lowerNeighbors::add);
1149////    sigma.getRoleNames().parallelStream().forEach(
1150////        r -> this.existentialRestrictions.parallelStream().filter(sD -> sD.first().equals(r)).map(Pair::second).forEach(
1151////            D -> D.lowerNeighbors2(sigma).parallelStream().forEach(
1152////                E -> recurseLowerNeighbors2(sigma, r, E, lowerNeighbors))));
1153////    return lowerNeighbors;
1154////  }
1155////
1156////  private final void recurseLowerNeighbors2(
1157////      final Signature sigma,
1158////      final IRI r,
1159////      final ELConceptDescription D,
1160////      final Set<ELConceptDescription> lowerNeighbors) {
1161////    if (D.upperNeighborsReduced().parallelStream().allMatch(
1162////        E -> ELConceptDescription.this.isSubsumedBy(ELConceptDescription.existentialRestriction(Pair.of(r, E))))) {
1163////      final ELConceptDescription lowerNeighbor = ELConceptDescription.this.clone();
1164////      lowerNeighbor.existentialRestrictions.add(Pair.of(r, D));
1165////      lowerNeighbors.add(lowerNeighbor);
1166////    } else
1167////      D.lowerNeighbors(sigma).parallelStream().forEach(E -> recurseLowerNeighbors2(sigma, r, E, lowerNeighbors));
1168////  }
1169//
1170//  public final Set<ELConceptDescription> lowerNeighbors4(final Signature sigma) {
1171//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1172//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1173//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1174//      final ELConceptDescription lowerNeighbor = C.clone();
1175//      lowerNeighbor.conceptNames.add(A);
1176//      return lowerNeighbor;
1177//    }).forEach(lowerNeighbors::add);
1178//    sigma.getRoleNames().parallelStream().forEach(r -> {
1179////      recurseLowerNeighbors4a(sigma, Collections.singletonList(r), C, lowerNeighbors);
1180//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1181//          .entries()
1182//          .parallelStream()
1183//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1184//          .map(Entry::getValue)
1185//          .collect(Collectors.toSet());
1186//      if (filter.isEmpty()) {
1187//        final ELConceptDescription lowerNeighbor = C.clone();
1188//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1189//        lowerNeighbors.add(lowerNeighbor);
1190//      } else {
1191//        if (filter.size() == 1) {
1192////          final ELConceptDescription D = filter.iterator().next();
1193////          for (ELConceptDescription E : D.lowerNeighbors4(sigma))
1194////            if (E.reduce().topLevelConjuncts() == 1)
1195////              lowerNeighbors
1196////                  .add(ELConceptDescription.conjunction(C.clone(), ELConceptDescription.existentialRestriction(r, E)));
1197//        } else
1198//          recurseLowerNeighbors4(
1199//              sigma,
1200//              r,
1201//              C,
1202//              Collections.singleton(ELConceptDescription.conjunction(filter).reduce()),
1203//              filter,
1204//              lowerNeighbors);
1205//        filter.parallelStream().forEach(F -> {
1206//          F.lowerNeighbors4(sigma).parallelStream().forEach(L -> {
1207//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1208//            L.existentialRestrictions.entries().removeIf(
1209//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1210//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1211//            if (!C.isSubsumedBy(rL))
1212//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1213//          });
1214//        });
1215//      }
1216//    });
1217//    return lowerNeighbors;
1218//  }
1219//
1220//  private final void recurseLowerNeighbors4(
1221//      final Signature sigma,
1222//      final IRI r,
1223//      final ELConceptDescription C,
1224//      final Set<ELConceptDescription> currentCandidates,
1225//      final Set<ELConceptDescription> filter,
1226//      final Set<ELConceptDescription> lowerNeighbors) {
1227//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1228//    currentCandidates.parallelStream().forEach(D -> {
1229////      D.reduce();
1230//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1231////      if (!C.isSubsumedBy(ELConceptDescription.existentialRestriction(r, D))) {
1232//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1233//        if (D.topLevelConjuncts() <= filter.size()
1234//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1235////          if (D.topLevelConjuncts() <= filter.size() && Us.parallelStream().allMatch(U -> C.isSubsumedBy(ELConceptDescription.existentialRestriction(r, U)))) {
1236//          final ELConceptDescription X = C.clone();
1237//          X.existentialRestrictions.put(r, D);
1238//          lowerNeighbors.add(X);
1239//        } else
1240//          Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1241//              // Us.parallelStream().filter(U -> !C.isSubsumedBy(ELConceptDescription.existentialRestriction(r,
1242//              // U))).forEach(
1243//              nextCandidates::add);
1244//      }
1245//    });
1246//    if (!nextCandidates.isEmpty())
1247//      recurseLowerNeighbors4(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1248//  }
1249//
1250//  private final void recurseLowerNeighbors4a(
1251//      final Signature sigma,
1252//      final List<IRI> rs,
1253//      final ELConceptDescription C,
1254//      final Set<ELConceptDescription> lowerNeighbors) {
1255//    final ELConceptDescription rsTop = ELConceptDescription.existentialRestriction(rs, ELConceptDescription.top());
1256//    if (C.isSubsumedBy(rsTop)) {
1257//      for (IRI A : sigma.getConceptNames()) {
1258//        final ELConceptDescription rsA =
1259//            ELConceptDescription.existentialRestriction(rs, ELConceptDescription.conceptName(A));
1260//        if (!C.isSubsumedBy(rsA))
1261//          lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rsA));
1262//      }
1263//      for (IRI r : sigma.getRoleNames()) {
1264//        final ELConceptDescription rsrTop = ELConceptDescription
1265//            .existentialRestriction(rs, ELConceptDescription.existentialRestriction(r, ELConceptDescription.top()));
1266//        if (!C.isSubsumedBy(rsrTop))
1267//          lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rsrTop));
1268//        final List<IRI> rsr = new LinkedList<>(rs);
1269//        rsr.add(r);
1270//        recurseLowerNeighbors4a(sigma, rsr, C, lowerNeighbors);
1271//      }
1272//    }
1273//  }
1274//
1275//  public final Set<ELConceptDescription> lowerNeighbors5(final Signature sigma) {
1276//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1277//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1278//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1279//      final ELConceptDescription lowerNeighbor = C.clone();
1280//      lowerNeighbor.conceptNames.add(A);
1281//      return lowerNeighbor;
1282//    }).forEach(lowerNeighbors::add);
1283//    sigma.getRoleNames().parallelStream().forEach(r -> {
1284//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1285//          .entries()
1286//          .parallelStream()
1287//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1288//          .map(Entry::getValue)
1289//          .collect(Collectors.toSet());
1290//      if (filter.isEmpty()) {
1291//        final ELConceptDescription lowerNeighbor = C.clone();
1292//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1293//        lowerNeighbors.add(lowerNeighbor);
1294//      } else {
1295//        if (filter.size() > 1) {
1296//          final Set<ELConceptDescription> firstCandidates = filter
1297//              .parallelStream()
1298//              .flatMap(
1299//                  F -> filter.parallelStream().filter(G -> !G.equals(F)).map(
1300//                      G -> ELConceptDescription.conjunction(F, G).reduce()))
1301//              .collect(Collectors.toSet());
1302//          recurseLowerNeighbors5(sigma, r, C, firstCandidates, filter, lowerNeighbors);
1303//        }
1304//        filter.parallelStream().forEach(F -> {
1305//          F.lowerNeighbors5(sigma).parallelStream().forEach(L -> {
1306//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1307//            L.existentialRestrictions.entries().removeIf(
1308//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1309//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1310//            if (!C.isSubsumedBy(rL))
1311//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1312//          });
1313//        });
1314//      }
1315//    });
1316//    return lowerNeighbors;
1317//  }
1318//
1319//  private final void recurseLowerNeighbors5(
1320//      final Signature sigma,
1321//      final IRI r,
1322//      final ELConceptDescription C,
1323//      final Set<ELConceptDescription> currentCandidates,
1324//      final Set<ELConceptDescription> filter,
1325//      final Set<ELConceptDescription> lowerNeighbors) {
1326//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1327//    currentCandidates.parallelStream().forEach(D -> {
1328////      D.reduce();
1329//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1330//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1331//        if (D.topLevelConjuncts() <= filter.size()
1332//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1333//          final ELConceptDescription X = C.clone();
1334//          X.existentialRestrictions.put(r, D);
1335//          lowerNeighbors.add(X);
1336//        } else
1337//          Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1338//              nextCandidates::add);
1339//      }
1340//    });
1341//    if (!nextCandidates.isEmpty())
1342//      recurseLowerNeighbors5(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1343//  }
1344//
1345//  public final Set<ELConceptDescription> lowerNeighbors6(final Signature sigma) {
1346//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1347//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1348//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1349//      final ELConceptDescription lowerNeighbor = C.clone();
1350//      lowerNeighbor.conceptNames.add(A);
1351//      return lowerNeighbor;
1352//    }).forEach(lowerNeighbors::add);
1353//    sigma.getRoleNames().parallelStream().forEach(r -> {
1354//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1355//          .entries()
1356//          .parallelStream()
1357//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1358//          .map(Entry::getValue)
1359//          .collect(Collectors.toSet());
1360//      if (filter.isEmpty()) {
1361//        final ELConceptDescription lowerNeighbor = C.clone();
1362//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1363//        lowerNeighbors.add(lowerNeighbor);
1364//      } else {
1365//        if (filter.size() > 1) {
1366//          final Set<ELConceptDescription> lowerNeighborsOfFilter = filter
1367//              .parallelStream()
1368//              .flatMap(F -> F.lowerNeighbors6(sigma).parallelStream())
1369//              .map(ELConceptDescription::reduce)
1370//              .collect(Collectors.toSet());
1371//          recurseLowerNeighbors6(sigma, r, C, lowerNeighborsOfFilter, filter, lowerNeighbors);
1372//        }
1373//        filter.parallelStream().forEach(F -> {
1374//          F.lowerNeighbors6(sigma).parallelStream().forEach(L -> {
1375//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1376//            L.existentialRestrictions.entries().removeIf(
1377//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1378//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1379//            if (!C.isSubsumedBy(rL))
1380//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1381//          });
1382//        });
1383//      }
1384//    });
1385//    return lowerNeighbors;
1386//  }
1387//
1388//  private final void recurseLowerNeighbors6(
1389//      final Signature sigma,
1390//      final IRI r,
1391//      final ELConceptDescription C,
1392//      final Set<ELConceptDescription> currentCandidates,
1393//      final Set<ELConceptDescription> filter,
1394//      final Set<ELConceptDescription> lowerNeighbors) {
1395//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1396//    currentCandidates.parallelStream().forEach(D -> {
1397////      D.reduce();
1398//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1399//        if (D.topLevelConjuncts() <= filter.size()) {
1400//          final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1401//          if (Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1402//            final ELConceptDescription X = C.clone();
1403//            X.existentialRestrictions.put(r, D);
1404//            lowerNeighbors.add(X);
1405//          } else
1406//            Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1407//                nextCandidates::add);
1408//        }
1409//      }
1410//    });
1411//    if (!nextCandidates.isEmpty())
1412//      recurseLowerNeighbors6(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1413//  }
1414//
1415//  public final Set<ELConceptDescription> lowerNeighbors7(final Signature sigma) {
1416//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1417//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1418//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1419//      final ELConceptDescription lowerNeighbor = C.clone();
1420//      lowerNeighbor.conceptNames.add(A);
1421//      return lowerNeighbor;
1422//    }).forEach(lowerNeighbors::add);
1423//    sigma.getRoleNames().parallelStream().forEach(r -> {
1424//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1425//          .entries()
1426//          .parallelStream()
1427//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1428//          .map(Entry::getValue)
1429//          .collect(Collectors.toSet());
1430//      if (filter.isEmpty()) {
1431//        final ELConceptDescription lowerNeighbor = C.clone();
1432//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1433//        lowerNeighbors.add(lowerNeighbor);
1434//      } else {
1435//        if (filter.size() > 1) {
1436//          final Set<ELConceptDescription> firstCandidates =
1437//              filter.parallelStream().flatMap(F -> filter.parallelStream().filter(G -> !G.equals(F)).map(G -> {
1438//                final ELConceptDescription F0 = F.clone();
1439//                final ELConceptDescription G0 = G.clone();
1440//                F0.conceptNames.removeIf(A -> G.isSubsumedBy(ELConceptDescription.conceptName(A)));
1441//                G0.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1442//                F0.existentialRestrictions.entries().removeIf(
1443//                    rE -> G.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1444//                G0.existentialRestrictions.entries().removeIf(
1445//                    rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1446//                return ELConceptDescription.conjunction(F0, G0).reduce();
1447//                // final ELConceptDescription D = ELConceptDescription.conjunction(F, G);
1448//                // D.reduce();
1449//                // D.conceptNames.removeIf(
1450//                // A -> F.isSubsumedBy(ELConceptDescription.conceptName(A))
1451//                // && G.isSubsumedBy(ELConceptDescription.conceptName(A)));
1452//                // D.existentialRestrictions.entries().removeIf(
1453//                // rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue()))
1454//                // && G.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1455//                // return D;
1456//                // final Set<IRI> commonNames = Stream
1457//                // .concat(
1458//                // F.getConceptNames().parallelStream().filter(A -> !G.getConceptNames().contains(A)),
1459//                // G.getConceptNames().parallelStream().filter(A -> !F.getConceptNames().contains(A)))
1460//                // .collect(Collectors.toSet());
1461//                // final HashMultimap<IRI, ELConceptDescription> commonExistentialRestrictions = HashMultimap.create();
1462//                // final SetMultimap<IRI, ELConceptDescription> _commonExistentialRestrictions =
1463//                // Multimaps.synchronizedSetMultimap(commonExistentialRestrictions);
1464//                // Stream
1465//                // .concat(
1466//                // F.getExistentialRestrictions().entries().parallelStream().filter(
1467//                // rX -> !G
1468//                // .isSubsumedBy(ELConceptDescription.existentialRestriction(rX.getKey(), rX.getValue()))),
1469//                // G.getExistentialRestrictions().entries().parallelStream().filter(
1470//                // rX -> !F
1471//                // .isSubsumedBy(ELConceptDescription.existentialRestriction(rX.getKey(), rX.getValue()))))
1472//                // .forEach(rX -> _commonExistentialRestrictions.put(rX.getKey(), rX.getValue()));
1473//                // return new ELConceptDescription(commonNames, commonExistentialRestrictions).reduce();
1474//              })).collect(Collectors.toSet());
1475//          recurseLowerNeighbors7(sigma, r, C, firstCandidates, filter, lowerNeighbors);
1476//        }
1477//        filter.parallelStream().forEach(F -> {
1478//          F.lowerNeighbors7(sigma).parallelStream().forEach(L -> {
1479//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1480//            L.existentialRestrictions.entries().removeIf(
1481//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1482//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1483//            if (!C.isSubsumedBy(rL))
1484//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1485//          });
1486//        });
1487//      }
1488//    });
1489//    return lowerNeighbors;
1490//  }
1491//
1492//  private final void recurseLowerNeighbors7(
1493//      final Signature sigma,
1494//      final IRI r,
1495//      final ELConceptDescription C,
1496//      final Set<ELConceptDescription> currentCandidates,
1497//      final Set<ELConceptDescription> filter,
1498//      final Set<ELConceptDescription> lowerNeighbors) {
1499//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1500//    currentCandidates.parallelStream().forEach(D -> {
1501////      D.reduce();
1502//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1503//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1504//        if (D.topLevelConjuncts() <= filter.size()
1505//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1506//          final ELConceptDescription X = C.clone();
1507//          X.existentialRestrictions.put(r, D);
1508//          lowerNeighbors.add(X);
1509//        } else
1510//          Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1511//              nextCandidates::add);
1512//      }
1513//    });
1514//    if (!nextCandidates.isEmpty())
1515//      recurseLowerNeighbors7(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1516//  }
1517//
1518//  public final Set<ELConceptDescription> lowerNeighbors8(final Signature sigma) {
1519//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1520//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1521//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1522//      final ELConceptDescription lowerNeighbor = C.clone();
1523//      lowerNeighbor.conceptNames.add(A);
1524//      return lowerNeighbor;
1525//    }).forEach(lowerNeighbors::add);
1526//    sigma.getRoleNames().parallelStream().forEach(r -> {
1527//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1528//          .entries()
1529//          .parallelStream()
1530//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1531//          .map(Entry::getValue)
1532//          .collect(Collectors.toSet());
1533//      if (filter.isEmpty()) {
1534//        final ELConceptDescription lowerNeighbor = C.clone();
1535//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1536//        lowerNeighbors.add(lowerNeighbor);
1537//      } else {
1538//        if (filter.size() > 1) {
1539//          final Set<ELConceptDescription> firstCandidates = Stream
1540//              .concat(
1541//                  filter.parallelStream().flatMap(F -> F.lowerNeighbors8(sigma).parallelStream()).map(
1542//                      ELConceptDescription::reduce),
1543//                  filter.parallelStream().flatMap(F -> filter.parallelStream().filter(G -> !G.equals(F)).map(G -> {
1544//                    final ELConceptDescription D = ELConceptDescription.conjunction(F, G);
1545//                    // final ELConceptDescription lcs = ELLeastCommonSubsumer.lcsOfMutuallyIncomparable(F, G);
1546//                    D.reduce();
1547//                    // lcs.reduce();
1548//                    D.conceptNames.removeIf(
1549//                        A -> F.isSubsumedBy(ELConceptDescription.conceptName(A))
1550//                            && G.isSubsumedBy(ELConceptDescription.conceptName(A)));
1551//                    D.existentialRestrictions.entries().removeIf(
1552//                        rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue()))
1553//                            && G.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1554//                    return D;
1555//                  })).filter(X -> filter.parallelStream().noneMatch(L -> X.isSubsumedBy(L))))
1556//              .collect(Collectors.toSet());
1557//          recurseLowerNeighbors8(sigma, r, C, firstCandidates, filter, lowerNeighbors);
1558//        }
1559//        filter.parallelStream().forEach(F -> {
1560//          F.lowerNeighbors8(sigma).parallelStream().forEach(L -> {
1561//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1562//            L.existentialRestrictions.entries().removeIf(
1563//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1564//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1565//            if (!C.isSubsumedBy(rL))
1566//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1567//          });
1568//        });
1569//      }
1570//    });
1571//    return lowerNeighbors;
1572//  }
1573//
1574//  private final void recurseLowerNeighbors8(
1575//      final Signature sigma,
1576//      final IRI r,
1577//      final ELConceptDescription C,
1578//      final Set<ELConceptDescription> currentCandidates,
1579//      final Set<ELConceptDescription> filter,
1580//      final Set<ELConceptDescription> lowerNeighbors) {
1581//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1582//    currentCandidates.parallelStream().forEach(D -> {
1583//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1584//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1585//        if (D.topLevelConjuncts() <= filter.size()
1586//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1587//          final ELConceptDescription X = C.clone();
1588//          X.existentialRestrictions.put(r, D);
1589//          lowerNeighbors.add(X);
1590//        } else
1591//          Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1592//              nextCandidates::add);
1593//      }
1594//    });
1595//    if (!nextCandidates.isEmpty())
1596//      recurseLowerNeighbors8(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1597//  }
1598//
1599//  public final Set<ELConceptDescription> lowerNeighbors9(final Signature sigma) {
1600//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1601//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1602//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1603//      final ELConceptDescription lowerNeighbor = C.clone();
1604//      lowerNeighbor.conceptNames.add(A);
1605//      return lowerNeighbor;
1606//    }).forEach(lowerNeighbors::add);
1607//    sigma.getRoleNames().parallelStream().forEach(r -> {
1608//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1609//          .entries()
1610//          .parallelStream()
1611//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1612//          .map(Entry::getValue)
1613//          .collect(Collectors.toSet());
1614//      if (filter.isEmpty()) {
1615//        final ELConceptDescription lowerNeighbor = C.clone();
1616//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1617//        lowerNeighbors.add(lowerNeighbor);
1618//      } else {
1619//        if (filter.size() > 1) {
1620//          final ELConceptDescription firstCandidate = ELConceptDescription.conjunction(filter);
1621//          firstCandidate.getConceptNames().removeIf(
1622//              A -> filter.parallelStream().allMatch(F -> F.getConceptNames().contains(A)));
1623//          firstCandidate.getExistentialRestrictions().entries().removeIf(rE -> {
1624//            final ELConceptDescription exrE = ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
1625//            return filter.parallelStream().allMatch(F -> F.isSubsumedBy(exrE));
1626//          });
1627//          recurseLowerNeighbors9(sigma, r, C, Collections.singleton(firstCandidate.reduce()), filter, lowerNeighbors);
1628//        }
1629//        filter.parallelStream().forEach(F -> {
1630//          F.lowerNeighbors9(sigma).parallelStream().forEach(L -> {
1631//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1632//            L.existentialRestrictions.entries().removeIf(
1633//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1634//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1635//            if (!C.isSubsumedBy(rL))
1636//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1637//          });
1638//        });
1639//      }
1640//    });
1641//    return lowerNeighbors;
1642//  }
1643//
1644//  private final void recurseLowerNeighbors9(
1645//      final Signature sigma,
1646//      final IRI r,
1647//      final ELConceptDescription C,
1648//      final Set<ELConceptDescription> currentCandidates,
1649//      final Set<ELConceptDescription> filter,
1650//      final Set<ELConceptDescription> lowerNeighbors) {
1651//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1652//    currentCandidates.parallelStream().forEach(D -> {
1653//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1654//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1655//        if (D.topLevelConjuncts() <= filter.size()
1656//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1657//          final ELConceptDescription X = C.clone();
1658//          X.existentialRestrictions.put(r, D);
1659//          lowerNeighbors.add(X);
1660//        } else
1661//          Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1662//              nextCandidates::add);
1663//      }
1664//    });
1665//    if (!nextCandidates.isEmpty())
1666//      recurseLowerNeighbors9(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1667//  }
1668//
1669//  public final Set<ELConceptDescription> lowerNeighbors10(final Signature sigma) {
1670//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1671//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1672//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1673//      final ELConceptDescription lowerNeighbor = C.clone();
1674//      lowerNeighbor.conceptNames.add(A);
1675//      return lowerNeighbor;
1676//    }).forEach(lowerNeighbors::add);
1677//    sigma.getRoleNames().parallelStream().forEach(r -> {
1678//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1679//          .entries()
1680//          .parallelStream()
1681//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1682//          .map(Entry::getValue)
1683//          .collect(Collectors.toSet());
1684//      if (filter.isEmpty()) {
1685//        final ELConceptDescription lowerNeighbor = C.clone();
1686//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1687//        lowerNeighbors.add(lowerNeighbor);
1688//      } else {
1689//        if (filter.size() > 1) {
1690//          final Set<IRI> forbiddenNames = filter
1691//              .parallelStream()
1692//              .flatMap(F -> F.getConceptNames().parallelStream())
1693//              .distinct()
1694//              .filter(A -> filter.parallelStream().allMatch(FF -> FF.getConceptNames().contains(A)))
1695//              .collect(Collectors.toSet());
1696//          final Set<Entry<IRI, ELConceptDescription>> forbiddenRestrictions = filter
1697//              .parallelStream()
1698//              .flatMap(F -> F.getExistentialRestrictions().entries().parallelStream())
1699//              .distinct()
1700//              .filter(rE -> {
1701//                final ELConceptDescription exrE =
1702//                    ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
1703//                return filter.parallelStream().allMatch(FF -> FF.isSubsumedBy(exrE));
1704//              })
1705//              .collect(Collectors.toSet());
1706//          final Set<ELConceptDescription> firstCandidates =
1707//              filter.parallelStream().flatMap(F -> filter.parallelStream().filter(G -> !G.equals(F)).map(G -> {
1708//                final ELConceptDescription that = ELConceptDescription.conjunction(F, G).reduce();
1709//                that.getConceptNames().removeAll(forbiddenNames);
1710//                that.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
1711//                // that.getConceptNames().removeIf(
1712//                // A -> filter.parallelStream().allMatch(FF -> FF.getConceptNames().contains(A)));
1713//                // that.getExistentialRestrictions().entries().removeIf(rE -> {
1714//                // final ELConceptDescription exrE =
1715//                // ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
1716//                // return filter.parallelStream().allMatch(FF -> FF.isSubsumedBy(exrE));
1717//                // });
1718//                return that;
1719//              })).collect(Collectors.toSet());
1720//          recurseLowerNeighbors10(sigma, r, C, firstCandidates, filter, lowerNeighbors);
1721//        }
1722//        filter.parallelStream().forEach(F -> {
1723//          F.lowerNeighbors10(sigma).parallelStream().forEach(L -> {
1724//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1725//            L.existentialRestrictions.entries().removeIf(
1726//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1727//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1728//            if (!C.isSubsumedBy(rL))
1729//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
1730//          });
1731//        });
1732//      }
1733//    });
1734//    return lowerNeighbors;
1735//  }
1736//
1737//  private final void recurseLowerNeighbors10(
1738//      final Signature sigma,
1739//      final IRI r,
1740//      final ELConceptDescription C,
1741//      final Set<ELConceptDescription> currentCandidates,
1742//      final Set<ELConceptDescription> filter,
1743//      final Set<ELConceptDescription> lowerNeighbors) {
1744//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
1745//    currentCandidates.parallelStream().forEach(D -> {
1746//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
1747//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
1748//        if (D.topLevelConjuncts() <= filter.size()
1749//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
1750//          final ELConceptDescription X = C.clone();
1751//          X.existentialRestrictions.put(r, D);
1752//          lowerNeighbors.add(X);
1753//        } else
1754//          Us.parallelStream().filter(U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))).forEach(
1755//              nextCandidates::add);
1756//      }
1757//    });
1758//    if (!nextCandidates.isEmpty())
1759//      recurseLowerNeighbors10(sigma, r, C, nextCandidates, filter, lowerNeighbors);
1760//  }
1761//
1762//  public final Set<ELConceptDescription> lowerNeighbors11(final Signature sigma) {
1763//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1764//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1765//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1766//      final ELConceptDescription lowerNeighbor = C.clone();
1767//      lowerNeighbor.conceptNames.add(A);
1768//      return lowerNeighbor;
1769//    }).forEach(lowerNeighbors::add);
1770//    sigma.getRoleNames().parallelStream().forEach(r -> {
1771//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1772//          .entries()
1773//          .parallelStream()
1774//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1775//          .map(Entry::getValue)
1776//          .collect(Collectors.toSet());
1777//      if (filter.isEmpty()) {
1778//        final ELConceptDescription lowerNeighbor = C.clone();
1779//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1780//        lowerNeighbors.add(lowerNeighbor);
1781//      } else {
1782//        if (filter.size() > 1) {
1783//          final Set<IRI> forbiddenNames = filter
1784//              .parallelStream()
1785//              .flatMap(F -> F.getConceptNames().parallelStream())
1786//              .distinct()
1787//              .filter(A -> filter.parallelStream().allMatch(FF -> FF.getConceptNames().contains(A)))
1788//              .collect(Collectors.toSet());
1789//          final Set<Entry<IRI, ELConceptDescription>> forbiddenRestrictions = filter
1790//              .parallelStream()
1791//              .flatMap(F -> F.getExistentialRestrictions().entries().parallelStream())
1792//              .distinct()
1793//              .filter(rE -> {
1794//                final ELConceptDescription exrE =
1795//                    ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
1796//                return filter.parallelStream().allMatch(FF -> FF.isSubsumedBy(exrE));
1797//              })
1798//              .collect(Collectors.toSet());
1799//          filter.parallelStream().map(F -> {
1800//            final ELConceptDescription that = F.clone();
1801//            that.getConceptNames().removeAll(forbiddenNames);
1802//            that.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
1803//            return that;
1804//          }).flatMap(F -> F.lowerNeighbors11(sigma).parallelStream()).forEach(L -> {
1805//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1806//            if (!C.isSubsumedBy(rL))
1807//              if (L.upperNeighborsReduced().parallelStream().allMatch(
1808//                  U -> C.isSubsumedBy(ELConceptDescription.existentialRestriction(r, U))))
1809//                lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));// reduce
1810//          });
1811//        }
1812//        filter.parallelStream().forEach(F -> {
1813//          F.lowerNeighbors11(sigma).parallelStream().forEach(L -> {
1814//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1815//            L.existentialRestrictions.entries().removeIf(
1816//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
1817//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
1818//            if (!C.isSubsumedBy(rL))
1819//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));// reduce
1820//          });
1821//        });
1822//      }
1823//    });
1824//    return lowerNeighbors;
1825//  }
1826//
1827//  public final Set<ELConceptDescription> lowerNeighbors12(final Signature sigma) {
1828//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1829//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1830//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1831//      final ELConceptDescription lowerNeighbor = C.clone();
1832//      lowerNeighbor.conceptNames.add(A);
1833//      return lowerNeighbor;
1834//    }).forEach(lowerNeighbors::add);
1835//    sigma.getRoleNames().parallelStream().forEach(r -> {
1836//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1837//          .entries()
1838//          .parallelStream()
1839//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1840//          .map(Entry::getValue)
1841//          .collect(Collectors.toSet());
1842//      if (filter.isEmpty()) {
1843//        final ELConceptDescription lowerNeighbor = C.clone();
1844//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1845//        lowerNeighbors.add(lowerNeighbor);
1846//      } else {
1847//        if (filter.size() > 1) {
1848//          final Set<IRI> forbiddenNames = filter
1849//              .parallelStream()
1850//              .flatMap(F -> F.getConceptNames().parallelStream())
1851//              .distinct()
1852//              .filter(A -> filter.parallelStream().allMatch(FF -> FF.getConceptNames().contains(A)))
1853//              .collect(Collectors.toSet());
1854//          final Set<Entry<IRI, ELConceptDescription>> forbiddenRestrictions = filter
1855//              .parallelStream()
1856//              .flatMap(F -> F.getExistentialRestrictions().entries().parallelStream())
1857//              .distinct()
1858//              .filter(rE -> {
1859//                final ELConceptDescription exrE =
1860//                    ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
1861//                return filter.parallelStream().allMatch(FF -> FF.isSubsumedBy(exrE));
1862//              })
1863//              .collect(Collectors.toSet());
1864//          if (forbiddenNames.isEmpty() && forbiddenRestrictions.isEmpty()) {
1865//            ELConceptDescription
1866//                .top()
1867//                .lowerNeighbors12(sigma)
1868//                .parallelStream()
1869//                .flatMap(
1870//                    X -> ELConceptDescription.top().lowerNeighbors12(sigma).parallelStream().map(
1871//                        Y -> X.and(Y).reduce()))
1872//                .forEach(L -> {
1873//                  final ELConceptDescription rL = L.exists(r);
1874//                  if (!C.isSubsumedBy(rL))
1875//                    if (L.upperNeighborsReduced().parallelStream().allMatch(U -> C.isSubsumedBy(U.exists(r))))
1876//                      lowerNeighbors.add(C.clone().and(rL));// reduce
1877//                });
1878//          }
1879//          filter.parallelStream().flatMap(F -> filter.parallelStream().filter(G -> !F.equals(G)).map(G -> {
1880//            final ELConceptDescription conj = F.and(G).reduce();
1881//            conj.getConceptNames().removeAll(forbiddenNames);
1882//            conj.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
1883//            final ELConceptDescription lcs = F.lcs(G).reduce();
1884//            return conj.lcs(lcs).reduce();
1885//          }))
1886//              .flatMap(
1887//                  H -> H.lowerNeighbors12(sigma).parallelStream().flatMap(
1888//                      K -> K.lowerNeighbors12(sigma).parallelStream()))
1889//              .forEach(L -> {
1890//                final ELConceptDescription rL = L.exists(r);
1891//                if (!C.isSubsumedBy(rL))
1892//                  if (L.upperNeighborsReduced().parallelStream().allMatch(U -> C.isSubsumedBy(U.exists(r))))
1893//                    lowerNeighbors.add(C.clone().and(rL));// reduce
1894//              });
1895////          filter.parallelStream().map(F -> {
1896////            final ELConceptDescription that = F.clone();
1897////            that.getConceptNames().removeAll(forbiddenNames);
1898////            that.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
1899////            return that;
1900////          }).flatMap(F -> F.upperNeighborsReduced().parallelStream().flatMap(U -> filter.parallelStream().map(G -> {
1901////            final ELConceptDescription that = G.clone();
1902////            that.getConceptNames().removeAll(forbiddenNames);
1903////            that.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
1904////            return that;
1905////          }).flatMap(G -> G.upperNeighborsReduced().parallelStream().map(V -> U.and(V).reduce()))))
1906////              // .map(H -> {
1907////              // H.getConceptNames().removeIf(A -> filter.parallelStream().allMatch(F ->
1908////              // F.getConceptNames().contains(A)));
1909////              // H.getExistentialRestrictions().entries().removeIf(
1910////              // rE -> filter.parallelStream().allMatch(F -> F.isSubsumedBy(rE.getValue().exists(rE.getKey()))));
1911////              // return H;
1912////              // })
1913////              .forEach(L -> {
1914////                final ELConceptDescription rL = L.exists(r);
1915////                if (!C.isSubsumedBy(rL))
1916////                  if (L.upperNeighborsReduced().parallelStream().allMatch(U -> C.isSubsumedBy(U.exists(r))))
1917////                    lowerNeighbors.add(C.clone().and(rL));// reduce
1918////              });
1919//////          filter.parallelStream().flatMap(F -> filter.parallelStream().filter(G -> !G.equals(F)).map(G -> {
1920//////            final ELConceptDescription that = ELConceptDescription.conjunction(F, G).reduce();
1921//////            that.getConceptNames().removeAll(forbiddenNames);
1922//////            that.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
1923//////            return that;
1924//////          })).forEach(L -> {
1925//////            final ELConceptDescription rL = L.exists(r);
1926//////            if (!C.isSubsumedBy(rL))
1927//////              if (L.upperNeighborsReduced().parallelStream().allMatch(U -> C.isSubsumedBy(U.exists(r))))
1928//////                lowerNeighbors.add(C.clone().and(rL));// reduce
1929//////          });
1930//        }
1931//        filter.parallelStream().forEach(F -> {
1932//          F.lowerNeighbors12(sigma).parallelStream().forEach(L -> {
1933//            final ELConceptDescription _rL = L.clone().exists(r);
1934//            if (!C.isSubsumedBy(_rL))
1935//              if (L.upperNeighborsReduced().parallelStream().allMatch(U -> C.isSubsumedBy(U.exists(r))))
1936//                lowerNeighbors.add(C.clone().and(_rL));// reduce
1937//
1938//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1939//            L.existentialRestrictions.entries().removeIf(rE -> F.isSubsumedBy(rE.getValue().exists(rE.getKey())));
1940//            final ELConceptDescription rL = L.exists(r);
1941//            if (!C.isSubsumedBy(rL))
1942//              lowerNeighbors.add(C.clone().and(rL));// reduce
1943//          });
1944//        });
1945//      }
1946//    });
1947//    return lowerNeighbors;
1948//  }
1949//
1950//  public final Set<ELConceptDescription> lowerNeighbors13(final Signature sigma) {
1951//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
1952//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
1953//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
1954//      final ELConceptDescription lowerNeighbor = C.clone();
1955//      lowerNeighbor.conceptNames.add(A);
1956//      return lowerNeighbor;
1957//    }).forEach(lowerNeighbors::add);
1958//    sigma.getRoleNames().parallelStream().forEach(r -> {
1959//      final Set<ELConceptDescription> filter = C.existentialRestrictions
1960//          .entries()
1961//          .parallelStream()
1962//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
1963//          .map(Entry::getValue)
1964//          .collect(Collectors.toSet());
1965//      if (filter.isEmpty()) {
1966//        final ELConceptDescription lowerNeighbor = C.clone();
1967//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
1968//        lowerNeighbors.add(lowerNeighbor);
1969//      } else {
1970//        if (filter.size() > 1) {
1971////          filter
1972////          .parallelStream()
1973////          .flatMap(
1974////              F -> F.upperNeighborsReduced().parallelStream().flatMap(
1975////                  U -> filter.parallelStream().filter(G -> !F.equals(G)).flatMap(
1976////                      G -> G.upperNeighborsReduced().parallelStream().map(V -> U.and(V).reduce()))))
1977//          filter
1978//              .parallelStream()
1979//              .flatMap(
1980//                  F -> F.upperNeighborsReduced().parallelStream().flatMap(
1981//                      U -> filter.parallelStream().map(G -> G.and(U).reduce())))
1982//              .map(H -> {
1983//                H.getConceptNames().removeIf(
1984//                    A -> filter.parallelStream().allMatch(F -> F.getConceptNames().contains(A)));
1985//                H.getExistentialRestrictions().entries().removeIf(
1986//                    rE -> filter.parallelStream().allMatch(F -> F.isSubsumedBy(rE.getValue().exists(rE.getKey()))));
1987//                return H;
1988//              })
1989//              .forEach(L -> {
1990//                final ELConceptDescription rL = L.exists(r);
1991//                if (!C.isSubsumedBy(rL))
1992//                  if (L.upperNeighborsReduced().parallelStream().allMatch(U -> C.isSubsumedBy(U.exists(r))))
1993//                    lowerNeighbors.add(C.clone().and(rL));// reduce
1994//              });
1995//        }
1996//        filter.parallelStream().forEach(F -> {
1997//          F.lowerNeighbors13(sigma).parallelStream().forEach(L -> {
1998//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
1999//            L.existentialRestrictions.entries().removeIf(rE -> F.isSubsumedBy(rE.getValue().exists(rE.getKey())));
2000//            final ELConceptDescription rL = L.exists(r);
2001//            if (!C.isSubsumedBy(rL))
2002//              lowerNeighbors.add(C.clone().and(rL));// reduce
2003//          });
2004//        });
2005//      }
2006//    });
2007//    return lowerNeighbors;
2008//  }
2009//
2010//  public final Set<ELConceptDescription> lowerNeighbors14(final Signature sigma) {
2011//    final ELConceptDescription C = ELConceptDescription.this.clone().reduce();
2012//    final Set<ELConceptDescription> lowerNeighbors = Sets.newConcurrentHashSet();
2013//    sigma.getConceptNames().parallelStream().filter(A -> !C.conceptNames.contains(A)).map(A -> {
2014//      final ELConceptDescription lowerNeighbor = C.clone();
2015//      lowerNeighbor.conceptNames.add(A);
2016//      return lowerNeighbor;
2017//    }).forEach(lowerNeighbors::add);
2018//    sigma.getRoleNames().parallelStream().forEach(r -> {
2019//      final Set<ELConceptDescription> filter = C.existentialRestrictions
2020//          .entries()
2021//          .parallelStream()
2022//          .filter(existentialRestriction -> existentialRestriction.getKey().equals(r))
2023//          .map(Entry::getValue)
2024//          .collect(Collectors.toSet());
2025//      if (filter.isEmpty()) {
2026//        final ELConceptDescription lowerNeighbor = C.clone();
2027//        lowerNeighbor.existentialRestrictions.put(r, ELConceptDescription.top());
2028//        lowerNeighbors.add(lowerNeighbor);
2029//      } else {
2030//        if (filter.size() > 1) {
2031//          final Set<IRI> forbiddenNames = filter
2032//              .parallelStream()
2033//              .flatMap(F -> F.getConceptNames().parallelStream())
2034//              .distinct()
2035//              .filter(A -> filter.parallelStream().allMatch(FF -> FF.getConceptNames().contains(A)))
2036//              .collect(Collectors.toSet());
2037//          final Set<Entry<IRI, ELConceptDescription>> forbiddenRestrictions = filter
2038//              .parallelStream()
2039//              .flatMap(F -> F.getExistentialRestrictions().entries().parallelStream())
2040//              .distinct()
2041//              .filter(rE -> {
2042//                final ELConceptDescription exrE =
2043//                    ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
2044//                return filter.parallelStream().allMatch(FF -> FF.isSubsumedBy(exrE));
2045//              })
2046//              .collect(Collectors.toSet());
2047//          final Set<ELConceptDescription> firstCandidates =
2048//              filter.parallelStream().flatMap(F -> filter.parallelStream().filter(G -> !G.equals(F)).map(G -> {
2049//                final ELConceptDescription that = ELConceptDescription.conjunction(F, G).reduce();
2050//                that.getConceptNames().removeAll(forbiddenNames);
2051//                that.getExistentialRestrictions().entries().removeAll(forbiddenRestrictions);
2052//                // that.getConceptNames().removeIf(
2053//                // A -> filter.parallelStream().allMatch(FF -> FF.getConceptNames().contains(A)));
2054//                // that.getExistentialRestrictions().entries().removeIf(rE -> {
2055//                // final ELConceptDescription exrE =
2056//                // ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue());
2057//                // return filter.parallelStream().allMatch(FF -> FF.isSubsumedBy(exrE));
2058//                // });
2059//                return that;
2060//              })).collect(Collectors.toSet());
2061//          recurseLowerNeighbors14(sigma, r, C, firstCandidates, filter, lowerNeighbors);
2062//        }
2063//        filter.parallelStream().forEach(F -> {
2064//          F.lowerNeighbors14(sigma).parallelStream().forEach(L -> {
2065//            L.conceptNames.removeIf(A -> F.isSubsumedBy(ELConceptDescription.conceptName(A)));
2066//            L.existentialRestrictions.entries().removeIf(
2067//                rE -> F.isSubsumedBy(ELConceptDescription.existentialRestriction(rE.getKey(), rE.getValue())));
2068//            final ELConceptDescription rL = ELConceptDescription.existentialRestriction(r, L);
2069//            if (!C.isSubsumedBy(rL))
2070//              lowerNeighbors.add(ELConceptDescription.conjunction(C.clone(), rL));
2071//          });
2072//        });
2073//      }
2074//    });
2075//    return lowerNeighbors;
2076//  }
2077//
2078//  private final void recurseLowerNeighbors14(
2079//      final Signature sigma,
2080//      final IRI r,
2081//      final ELConceptDescription C,
2082//      final Set<ELConceptDescription> currentCandidates,
2083//      final Set<ELConceptDescription> filter,
2084//      final Set<ELConceptDescription> lowerNeighbors) {
2085//    final Set<ELConceptDescription> nextCandidates = Sets.newConcurrentHashSet();
2086//    currentCandidates.parallelStream().forEach(D -> {
2087//      if (filter.parallelStream().noneMatch(F -> F.isSubsumedBy(D))) {
2088//        final Set<ELConceptDescription> Us = D.upperNeighborsReduced();
2089//        if (D.topLevelConjuncts() <= filter.size()
2090//            && Us.parallelStream().allMatch(U -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(U)))) {
2091//          final ELConceptDescription X = C.clone();
2092//          X.existentialRestrictions.put(r, D);
2093//          lowerNeighbors.add(X);
2094//        } else
2095//          Us
2096//              .parallelStream()
2097//              .filter(
2098//                  U -> filter.parallelStream().noneMatch(F -> F.isSubsumedBy(U))
2099//                      && U.upperNeighborsReduced().parallelStream().anyMatch(
2100//                          V -> filter.parallelStream().anyMatch(F -> F.isSubsumedBy(V))))
2101//              .forEach(nextCandidates::add);
2102//      }
2103//    });
2104//    if (!nextCandidates.isEmpty())
2105//      recurseLowerNeighbors14(sigma, r, C, nextCandidates, filter, lowerNeighbors);
2106//  }
2107
2108  @Override
2109  public final String toString() {
2110    if (isBot())
2111      return UnicodeSymbols.BOT;
2112    if (isTop())
2113      return UnicodeSymbols.TOP;
2114    final StringBuilder sb = new StringBuilder();
2115    final Iterator<IRI> it1 = conceptNames.iterator();
2116    if (it1.hasNext()) {
2117      sb.append(it1.next().toString());
2118    }
2119    while (it1.hasNext()) {
2120      // sb.append(" ");
2121      sb.append(UnicodeSymbols.SQCAP);
2122      // sb.append(" ");
2123      sb.append(it1.next().toString());
2124    }
2125    if (!conceptNames.isEmpty() && !existentialRestrictions.isEmpty()) {
2126      // sb.append(" ");
2127      sb.append(UnicodeSymbols.SQCAP);
2128      // sb.append(" ");
2129    }
2130    final Iterator<Entry<IRI, ELConceptDescription>> it2 = existentialRestrictions.entries().iterator();
2131    if (it2.hasNext()) {
2132      final Entry<IRI, ELConceptDescription> existentialRestriction = it2.next();
2133      sb.append(UnicodeSymbols.EXISTS);
2134      // sb.append(" ");
2135      sb.append(existentialRestriction.getKey().toString());
2136      // sb.append(" ");
2137      sb.append(".");
2138      if (existentialRestriction.getValue().conceptNames.size()
2139          + existentialRestriction.getValue().existentialRestrictions.size() <= 1)
2140        sb.append(existentialRestriction.getValue().toString());
2141      else {
2142        sb.append("(");
2143        sb.append(existentialRestriction.getValue().toString());
2144        sb.append(")");
2145      }
2146    }
2147    while (it2.hasNext()) {
2148      // sb.append(" ");
2149      sb.append(UnicodeSymbols.SQCAP);
2150      final Entry<IRI, ELConceptDescription> existentialRestriction = it2.next();
2151      // sb.append(" ");
2152      sb.append(UnicodeSymbols.EXISTS);
2153      // sb.append(" ");
2154      sb.append(existentialRestriction.getKey().toString());
2155      // sb.append(" ");
2156      sb.append(".");
2157      if (existentialRestriction.getValue().conceptNames.size()
2158          + existentialRestriction.getValue().existentialRestrictions.size() <= 1)
2159        sb.append(existentialRestriction.getValue().toString());
2160      else {
2161        sb.append("(");
2162        sb.append(existentialRestriction.getValue().toString());
2163        sb.append(")");
2164      }
2165    }
2166    return sb.toString();
2167  }
2168
2169  private final String toShortString(IRI iri) {
2170    return iri.toString().substring(iri.toString().indexOf("#") + 1);
2171  }
2172
2173  public final String toShortString() {
2174    if (isBot())
2175      return UnicodeSymbols.BOT;
2176    if (isTop())
2177      return UnicodeSymbols.TOP;
2178    final StringBuilder sb = new StringBuilder();
2179    final Iterator<IRI> it1 = conceptNames.iterator();
2180    if (it1.hasNext()) {
2181      sb.append(toShortString(it1.next()));
2182    }
2183    while (it1.hasNext()) {
2184      // sb.append(" ");
2185      sb.append(UnicodeSymbols.SQCAP);
2186      // sb.append(" ");
2187      sb.append(toShortString(it1.next()));
2188    }
2189    if (!conceptNames.isEmpty() && !existentialRestrictions.isEmpty()) {
2190      // sb.append(" ");
2191      sb.append(UnicodeSymbols.SQCAP);
2192      // sb.append(" ");
2193    }
2194    final Iterator<Entry<IRI, ELConceptDescription>> it2 = existentialRestrictions.entries().iterator();
2195    if (it2.hasNext()) {
2196      final Entry<IRI, ELConceptDescription> existentialRestriction = it2.next();
2197      sb.append(UnicodeSymbols.EXISTS);
2198      // sb.append(" ");
2199      sb.append(toShortString(existentialRestriction.getKey()));
2200      // sb.append(" ");
2201      sb.append(".");
2202      if (existentialRestriction.getValue().conceptNames.size()
2203          + existentialRestriction.getValue().existentialRestrictions.size() <= 1)
2204        sb.append(existentialRestriction.getValue().toShortString());
2205      else {
2206        sb.append("(");
2207        sb.append(existentialRestriction.getValue().toShortString());
2208        sb.append(")");
2209      }
2210    }
2211    while (it2.hasNext()) {
2212      // sb.append(" ");
2213      sb.append(UnicodeSymbols.SQCAP);
2214      final Entry<IRI, ELConceptDescription> existentialRestriction = it2.next();
2215      // sb.append(" ");
2216      sb.append(UnicodeSymbols.EXISTS);
2217      // sb.append(" ");
2218      sb.append(toShortString(existentialRestriction.getKey()));
2219      // sb.append(" ");
2220      sb.append(".");
2221      if (existentialRestriction.getValue().conceptNames.size()
2222          + existentialRestriction.getValue().existentialRestrictions.size() <= 1)
2223        sb.append(existentialRestriction.getValue().toShortString());
2224      else {
2225        sb.append("(");
2226        sb.append(existentialRestriction.getValue().toShortString());
2227        sb.append(")");
2228      }
2229    }
2230    return sb.toString();
2231  }
2232
2233  public final String toLaTeXString() {
2234    if (this.isBot())
2235      return "\\bot";
2236    if (this.isTop())
2237      return "\\top";
2238    final StringBuilder sb = new StringBuilder();
2239    final Iterator<IRI> it1 = conceptNames.iterator();
2240    if (it1.hasNext()) {
2241      sb.append(it1.next().toString());
2242    }
2243    while (it1.hasNext()) {
2244      sb.append(" \\sqcap ");
2245      sb.append(it1.next().toString());
2246    }
2247    final Iterator<Entry<IRI, ELConceptDescription>> it2 = existentialRestrictions.entries().iterator();
2248    if (conceptNames.isEmpty())
2249      sb.append(" \\sqcap ");
2250    if (it2.hasNext()) {
2251      final Entry<IRI, ELConceptDescription> existentialRestriction = it2.next();
2252      sb.append(" \\exists ");
2253      sb.append(existentialRestriction.getKey().toString());
2254      sb.append(" . ");
2255      if (existentialRestriction.getValue().conceptNames.size()
2256          + existentialRestriction.getValue().existentialRestrictions.size() <= 1)
2257        sb.append(existentialRestriction.getValue().toLaTeXString());
2258      else {
2259        sb.append(" \\left( ");
2260        sb.append(existentialRestriction.getValue().toLaTeXString());
2261        sb.append(" \\right) ");
2262      }
2263    }
2264    while (it2.hasNext()) {
2265      final Entry<IRI, ELConceptDescription> existentialRestriction = it2.next();
2266      sb.append(" \\sqcap ");
2267      sb.append(" \\exists ");
2268      sb.append(existentialRestriction.getKey().toString());
2269      sb.append(" . ");
2270      if (existentialRestriction.getValue().conceptNames.size()
2271          + existentialRestriction.getValue().existentialRestrictions.size() <= 1)
2272        sb.append(existentialRestriction.getValue().toLaTeXString());
2273      else {
2274        sb.append(" \\left( ");
2275        sb.append(existentialRestriction.getValue().toLaTeXString());
2276        sb.append(" \\right) ");
2277      }
2278
2279    }
2280    return sb.toString();
2281  }
2282
2283  @Override
2284  public final boolean equals(Object obj) {
2285    if (obj == null)
2286      return false;
2287    if (!(obj instanceof ELConceptDescription))
2288      return false;
2289    final ELConceptDescription other = (ELConceptDescription) obj;
2290    return this.conceptNames.equals(other.conceptNames)
2291        && this.existentialRestrictions.equals(other.existentialRestrictions);
2292  }
2293
2294  @Override
2295  public final int hashCode() {
2296    return 2 * conceptNames.hashCode() + 3 * existentialRestrictions.hashCode();
2297  }
2298
2299  @Override
2300  public final ELConceptDescription clone() {
2301    final ELConceptDescription clone = new ELConceptDescription();
2302    clone.getConceptNames().addAll(this.getConceptNames());
2303    this
2304        .getExistentialRestrictions()
2305        .entries()
2306        .parallelStream()
2307        .map(ER -> Pair.of(ER.getKey(), ER.getValue().clone()))
2308        .sequential()
2309        .forEach(ER -> clone.getExistentialRestrictions().put(ER.x(), ER.y()));
2310    return clone;
2311  }
2312
2313  @Override
2314  public final int compareTo(final ELConceptDescription other) {
2315    final List<Boolean> x = Stream
2316        .<Supplier<Boolean>> of(() -> this.subsumes(other), () -> other.subsumes(this))
2317        .parallel()
2318        .map(Supplier::get)
2319        .collect(Collectors.toList());
2320    if (x.get(0) && x.get(1))
2321      return 0;
2322    else if (x.get(0))
2323      return 1;
2324    else if (x.get(1))
2325      return -1;
2326    else
2327      return Integer.MAX_VALUE;
2328  }
2329
2330  @Override
2331  public final boolean equivalent(final ELConceptDescription other) {
2332    return Stream
2333        .<Supplier<Boolean>> of(() -> this.subsumes(other), () -> other.subsumes(this))
2334        .parallel()
2335        .allMatch(Supplier::get);
2336  }
2337
2338  @Override
2339  public final boolean smaller(final ELConceptDescription other) {
2340    return Stream
2341        .<Supplier<Boolean>> of(() -> !this.subsumes(other), () -> other.subsumes(this))
2342        .parallel()
2343        .allMatch(Supplier::get);
2344  }
2345
2346  @Override
2347  public final boolean greater(final ELConceptDescription other) {
2348    return Stream
2349        .<Supplier<Boolean>> of(() -> this.subsumes(other), () -> !other.subsumes(this))
2350        .parallel()
2351        .allMatch(Supplier::get);
2352  }
2353
2354  @Override
2355  public final boolean smallerEq(final ELConceptDescription other) {
2356    return this.isSubsumedBy(other);
2357  }
2358
2359  @Override
2360  public final boolean greaterEq(final ELConceptDescription other) {
2361    return this.subsumes(other);
2362  }
2363
2364  @Override
2365  public final boolean uncomparable(final ELConceptDescription other) {
2366    return Stream
2367        .<Supplier<Boolean>> of(() -> !this.subsumes(other), () -> !other.subsumes(this))
2368        .parallel()
2369        .allMatch(Supplier::get);
2370  }
2371
2372  @Override
2373  public ELConceptDescription infimum(ELConceptDescription e) {
2374    return and(e);
2375  }
2376
2377  @Override
2378  public ELConceptDescription supremum(ELConceptDescription e) {
2379    return lcs(e);
2380  }
2381
2382  @Override
2383  public boolean inf(ELConceptDescription e) {
2384    if (smallerEq(e))
2385      return false;
2386    else {
2387      conceptNames.addAll(e.conceptNames);
2388      existentialRestrictions.putAll(e.existentialRestrictions);
2389      return true;
2390    }
2391  }
2392
2393  @Override
2394  public boolean sup(ELConceptDescription e) {
2395    if (greaterEq(e))
2396      return false;
2397    else {
2398      final ELConceptDescription supremum = supremum(e);
2399      conceptNames.clear();
2400      existentialRestrictions.clear();
2401      conceptNames.addAll(supremum.conceptNames);
2402      existentialRestrictions.putAll(supremum.existentialRestrictions);
2403      return true;
2404    }
2405  }
2406
2407  @Override
2408  public ELConceptDescription greatest() {
2409    return ELConceptDescription.top();
2410  }
2411
2412  @Override
2413  public ELConceptDescription smallest() {
2414    return ELConceptDescription.bot();
2415  }
2416
2417}