001/**
002 * @author Francesco.Kriegel@gmx.de
003 */
004package conexp.fx.core.context;
005
006/*
007 * #%L
008 * Concept Explorer FX
009 * %%
010 * Copyright (C) 2010 - 2023 Francesco Kriegel
011 * %%
012 * This program is free software: you can redistribute it and/or modify
013 * it under the terms of the GNU General Public License as
014 * published by the Free Software Foundation, either version 3 of the
015 * License, or (at your option) any later version.
016 * 
017 * This program is distributed in the hope that it will be useful,
018 * but WITHOUT ANY WARRANTY; without even the implied warranty of
019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
020 * GNU General Public License for more details.
021 * 
022 * You should have received a copy of the GNU General Public
023 * License along with this program.  If not, see
024 * <http://www.gnu.org/licenses/gpl-3.0.html>.
025 * #L%
026 */
027
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.HashSet;
032import java.util.Set;
033import java.util.stream.Collectors;
034import java.util.stream.Stream;
035
036import org.ujmp.core.Matrix;
037import org.ujmp.core.booleanmatrix.BooleanMatrix;
038import org.ujmp.core.calculation.Calculation.Ret;
039
040import com.google.common.base.Function;
041import com.google.common.base.Functions;
042import com.google.common.base.Predicate;
043import com.google.common.base.Predicates;
044import com.google.common.collect.Collections2;
045import com.google.common.collect.Sets;
046import com.google.common.primitives.Ints;
047
048import conexp.fx.core.algorithm.nextclosure.NextConcept;
049import conexp.fx.core.collections.Collections3;
050import conexp.fx.core.collections.Pair;
051import conexp.fx.core.collections.relation.AbstractRelation;
052import conexp.fx.core.collections.relation.MatrixRelation;
053import conexp.fx.core.collections.relation.Relation;
054import conexp.fx.core.collections.relation.RelationEvent;
055import conexp.fx.core.collections.setlist.HashSetArrayList;
056import conexp.fx.core.collections.setlist.SetList;
057import conexp.fx.core.collections.setlist.SetLists;
058import conexp.fx.core.math.BooleanMatrices;
059import conexp.fx.core.math.GuavaIsomorphism;
060import conexp.fx.core.util.Constants;
061import javafx.beans.property.SimpleStringProperty;
062import javafx.beans.property.StringProperty;
063
064public class MatrixContext<G, M> extends MatrixRelation<G, M> implements Context<G, M> {
065
066  public enum Incidence {
067    CROSS(Constants.CROSS_CHARACTER),
068    DOWN_ARROW(Constants.DOWN_ARROW_CHARACTER),
069    UP_ARROW(Constants.UP_ARROW_CHARACTER),
070    BOTH_ARROW(Constants.BOTH_ARROW_CHARACTER),
071    DOWN_PATH(Constants.DOUBLE_DOWN_ARROW_CHARACTER),
072    UP_PATH(Constants.DOUBLE_UP_ARROW_CHARACTER),
073    BOTH_PATH(Constants.DOUBLE_BOTH_ARROW_CHARACTER),
074    NO_CROSS(Constants.NO_CROSS_CHARACTER);
075
076    private final String character;
077
078    private Incidence(final String character) {
079      this.character = character;
080    }
081
082    public final String toString() {
083      return character;
084    }
085  }
086
087  public final StringProperty                         id                      = new SimpleStringProperty(null);
088  public MatrixContext<G, M>                          selection               = this;
089  public final SetList<Set<Integer>>                  _objects                = new HashSetArrayList<Set<Integer>>();
090  public final SetList<Set<Integer>>                  _attributes             = new HashSetArrayList<Set<Integer>>();
091  private final Collection<Integer>                   i                       =
092      Collections2.transform(_objects, Collections3.<Integer> firstElement());
093  private final Collection<Integer>                   j                       =
094      Collections2.transform(_attributes, Collections3.<Integer> firstElement());
095  public final Context<Set<Integer>, Set<Integer>>    _cleaned                =
096      new AbstractContext<Set<Integer>, Set<Integer>>(_objects, _attributes, false) {
097
098                                                                                    @SuppressWarnings("unchecked")
099                                                                                    public final boolean contains(
100                                                                                        final Object _i,
101                                                                                        final Object _j) {
102                                                                                      return matrix.getBoolean(
103                                                                                          (long) Collections3
104                                                                                              .firstElement(
105                                                                                                  (Set<Integer>) _i),
106                                                                                          (long) Collections3
107                                                                                              .firstElement(
108                                                                                                  (Set<Integer>) _j));
109                                                                                    }
110
111                                                                                    public final
112                                                                                        MatrixContext<Set<Integer>, Set<Integer>>
113                                                                                        clone() {
114                                                                                      return new MatrixContext<Set<Integer>, Set<Integer>>(
115                                                                                          _objects,
116                                                                                          _attributes,
117                                                                                          (BooleanMatrix) matrix
118                                                                                              .selectRows(Ret.NEW, i)
119                                                                                              .selectColumns(
120                                                                                                  Ret.NEW,
121                                                                                                  j),
122                                                                                          false);
123                                                                                    }
124                                                                                  };
125  public Relation<Set<Integer>, Set<Integer>>         _downArrows             =
126      new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) {
127
128                                                                                    public final boolean contains(
129                                                                                        final Object object,
130                                                                                        final Object attribute) {
131                                                                                      @SuppressWarnings("unchecked")
132                                                                                      final int rowIndex =
133                                                                                          Collections3.firstElement(
134                                                                                              (Set<Integer>) object);
135                                                                                      @SuppressWarnings("unchecked")
136                                                                                      final int columnIndex =
137                                                                                          Collections3.firstElement(
138                                                                                              (Set<Integer>) attribute);
139                                                                                      if (!matrix.getBoolean(
140                                                                                          rowIndex,
141                                                                                          columnIndex))
142                                                                                        return _col(columnIndex, i)
143                                                                                            .containsAll(
144                                                                                                _extent(
145                                                                                                    Collections
146                                                                                                        .singleton(
147                                                                                                            rowIndex),
148                                                                                                    Collections2.filter(
149                                                                                                        i,
150                                                                                                        Predicates.not(
151                                                                                                            Predicates
152                                                                                                                .equalTo(
153                                                                                                                    rowIndex))),
154                                                                                                    j));
155                                                                                      return false;
156                                                                                    }
157                                                                                  };
158  public Relation<Set<Integer>, Set<Integer>>         _upArrows               =
159      new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) {
160
161                                                                                    public final boolean contains(
162                                                                                        final Object object,
163                                                                                        final Object attribute) {
164                                                                                      @SuppressWarnings("unchecked")
165                                                                                      final int _i =
166                                                                                          Collections3.firstElement(
167                                                                                              (Set<Integer>) object);
168                                                                                      @SuppressWarnings("unchecked")
169                                                                                      final int _j =
170                                                                                          Collections3.firstElement(
171                                                                                              (Set<Integer>) attribute);
172                                                                                      if (!matrix.getBoolean(_i, _j))
173                                                                                        return _row(_i, j).containsAll(
174                                                                                            _intent(
175                                                                                                Collections
176                                                                                                    .singleton(_j),
177                                                                                                i,
178                                                                                                Collections2.filter(
179                                                                                                    j,
180                                                                                                    Predicates.not(
181                                                                                                        Predicates
182                                                                                                            .equalTo(
183                                                                                                                _j)))));
184                                                                                      return false;
185                                                                                    }
186                                                                                  };;
187  public Relation<Set<Integer>, Set<Integer>>         _downPaths;
188  public Relation<Set<Integer>, Set<Integer>>         _upPaths;
189  private final Predicate<Set<Integer>>               _isIrreducibleObject    = new Predicate<Set<Integer>>() {
190
191                                                                                public final boolean
192                                                                                    apply(final Set<Integer> i) {
193                                                                                  for (Set<Integer> j : _attributes)
194                                                                                    if (_downArrows.contains(i, j))
195                                                                                      return true;
196                                                                                  return false;
197                                                                                }
198                                                                              };
199  private final Predicate<Set<Integer>>               _isIrreducibleAttribute = new Predicate<Set<Integer>>() {
200
201                                                                                public final boolean
202                                                                                    apply(final Set<Integer> j) {
203                                                                                  for (Set<Integer> i : _objects)
204                                                                                    if (_upArrows.contains(i, j))
205                                                                                      return true;
206                                                                                  return false;
207                                                                                }
208                                                                              };
209  public final SetList<Set<Integer>>                  _irreducibleObjects     = new HashSetArrayList<Set<Integer>>();
210  public final SetList<Set<Integer>>                  _irreducibleAttributes  = new HashSetArrayList<Set<Integer>>();
211  public final Context<Set<Integer>, Set<Integer>>    _reduced                =
212      new AbstractContext<Set<Integer>, Set<Integer>>(_irreducibleObjects, _irreducibleAttributes, false) {
213
214                                                                                    @SuppressWarnings("unchecked")
215                                                                                    public final boolean contains(
216                                                                                        final Object _i,
217                                                                                        final Object _j) {
218                                                                                      return matrix.getBoolean(
219                                                                                          (long) Collections3
220                                                                                              .firstElement(
221                                                                                                  (Set<Integer>) _i),
222                                                                                          (long) Collections3
223                                                                                              .firstElement(
224                                                                                                  (Set<Integer>) _j));
225                                                                                    }
226
227                                                                                    public final
228                                                                                        MatrixContext<Set<Integer>, Set<Integer>>
229                                                                                        clone() {
230                                                                                      return new MatrixContext<Set<Integer>, Set<Integer>>(
231                                                                                          _irreducibleObjects,
232                                                                                          _irreducibleAttributes,
233                                                                                          (BooleanMatrix) matrix
234                                                                                              .selectRows(
235                                                                                                  Ret.NEW,
236                                                                                                  Collections2
237                                                                                                      .transform(
238                                                                                                          _irreducibleObjects,
239                                                                                                          Collections3
240                                                                                                              .<Integer> firstElement()))
241                                                                                              .selectColumns(
242                                                                                                  Ret.NEW,
243                                                                                                  Collections2
244                                                                                                      .transform(
245                                                                                                          _irreducibleAttributes,
246                                                                                                          Collections3
247                                                                                                              .<Integer> firstElement())),
248                                                                                          false);
249                                                                                    }
250                                                                                  };;
251  public final Function<Iterable<Integer>, G>         _firstObject            =
252      Functions.compose(rowHeads.indexGuava().inverse(), Collections3.<Integer> firstElement());
253  public final Function<Iterable<Integer>, M>         _firstAttribute         =
254      Functions.compose(colHeads.indexGuava().inverse(), Collections3.<Integer> firstElement());
255  public final GuavaIsomorphism<Set<Integer>, Set<G>> _allObjects             =
256      new GuavaIsomorphism<Set<Integer>, Set<G>>() {
257
258                                                                                    public final Set<G>
259                                                                                        apply(final Set<Integer> set) {
260                                                                                      return Collections3.transform(
261                                                                                          set,
262                                                                                          GuavaIsomorphism.invert(
263                                                                                              rowHeads().indexGuava()));
264                                                                                    }
265
266                                                                                    public Set<Integer>
267                                                                                        invert(final Set<G> set) {
268                                                                                      return Collections3.transform(
269                                                                                          set,
270                                                                                          rowHeads().indexGuava());
271                                                                                    }
272                                                                                  };
273  public final GuavaIsomorphism<Set<Integer>, Set<M>> _allAttributes          =
274      new GuavaIsomorphism<Set<Integer>, Set<M>>() {
275
276                                                                                    public final Set<M>
277                                                                                        apply(final Set<Integer> set) {
278                                                                                      return Collections3.transform(
279                                                                                          set,
280                                                                                          GuavaIsomorphism.invert(
281                                                                                              colHeads().indexGuava()));
282                                                                                    }
283
284                                                                                    public Set<Integer>
285                                                                                        invert(final Set<M> set) {
286                                                                                      return Collections3.transform(
287                                                                                          set,
288                                                                                          colHeads().indexGuava());
289                                                                                    }
290                                                                                  };
291  public final AbstractContext<Set<G>, Set<M>>        cleaned                 = new AbstractContext<Set<G>, Set<M>>(
292      SetLists.transform(_objects, _allObjects),
293      SetLists.transform(_attributes, _allAttributes),
294      false) {
295
296                                                                                @SuppressWarnings("unchecked")
297                                                                                public boolean
298                                                                                    contains(Object o1, Object o2) {
299                                                                                  return _cleaned.contains(
300                                                                                      _allObjects.invert((Set<G>) o1),
301                                                                                      _allAttributes
302                                                                                          .invert((Set<M>) o2));
303                                                                                }
304
305                                                                                public MatrixContext<Set<G>, Set<M>>
306                                                                                    clone() {
307                                                                                  return new MatrixContext<Set<G>, Set<M>>(
308                                                                                      this.rowHeads().clone(),
309                                                                                      this.colHeads().clone(),
310                                                                                      _cleaned.clone().matrix,
311                                                                                      false);
312                                                                                };
313                                                                              };
314  public final AbstractContext<Set<G>, Set<M>>        reduced                 = new AbstractContext<Set<G>, Set<M>>(
315      SetLists.transform(_irreducibleObjects, _allObjects),
316      SetLists.transform(_irreducibleAttributes, _allAttributes),
317      false) {
318
319                                                                                @SuppressWarnings("unchecked")
320                                                                                public boolean
321                                                                                    contains(Object o1, Object o2) {
322                                                                                  return _reduced.contains(
323                                                                                      _allObjects.invert((Set<G>) o1),
324                                                                                      _allAttributes
325                                                                                          .invert((Set<M>) o2));
326                                                                                }
327                                                                              };
328  public final AbstractRelation<Set<G>, Set<M>>       downArrows              = new AbstractRelation<Set<G>, Set<M>>(
329      SetLists.transform(_objects, _allObjects),
330      SetLists.transform(_attributes, _allAttributes),
331      false) {
332
333                                                                                @SuppressWarnings("unchecked")
334                                                                                public boolean
335                                                                                    contains(Object o1, Object o2) {
336                                                                                  return _downArrows.contains(
337                                                                                      _allObjects.invert((Set<G>) o1),
338                                                                                      _allAttributes
339                                                                                          .invert((Set<M>) o2));
340                                                                                }
341                                                                              };
342  public final AbstractRelation<Set<G>, Set<M>>       upArrows                = new AbstractRelation<Set<G>, Set<M>>(
343      SetLists.transform(_objects, _allObjects),
344      SetLists.transform(_attributes, _allAttributes),
345      false) {
346
347                                                                                @SuppressWarnings("unchecked")
348                                                                                public boolean
349                                                                                    contains(Object o1, Object o2) {
350                                                                                  return _upArrows.contains(
351                                                                                      _allObjects.invert((Set<G>) o1),
352                                                                                      _allAttributes
353                                                                                          .invert((Set<M>) o2));
354                                                                                }
355                                                                              };
356  public final AbstractRelation<G, M>                 DownArrows              =
357      new AbstractRelation<G, M>(rowHeads, colHeads, false) {
358
359                                                                                    public boolean
360                                                                                        contains(Object o1, Object o2) {
361                                                                                      return _downArrows.contains(
362                                                                                          _objectEquivalence().col(
363                                                                                              rowHeads().indexOf(o1)),
364                                                                                          _attributeEquivalence().col(
365                                                                                              colHeads().indexOf(o2)));
366                                                                                    }
367                                                                                  };
368  public final AbstractRelation<G, M>                 UpArrows                =
369      new AbstractRelation<G, M>(rowHeads, colHeads, false) {
370
371                                                                                    public boolean
372                                                                                        contains(Object o1, Object o2) {
373                                                                                      return _upArrows.contains(
374                                                                                          _objectEquivalence().col(
375                                                                                              rowHeads().indexOf(o1)),
376                                                                                          _attributeEquivalence().col(
377                                                                                              colHeads().indexOf(o2)));
378                                                                                    }
379                                                                                  };
380  public final AbstractRelation<Set<G>, Set<M>>       downPaths               = new AbstractRelation<Set<G>, Set<M>>(
381      SetLists.transform(_objects, _allObjects),
382      SetLists.transform(_attributes, _allAttributes),
383      false) {
384
385                                                                                @SuppressWarnings("unchecked")
386                                                                                public boolean
387                                                                                    contains(Object o1, Object o2) {
388                                                                                  return _downPaths.contains(
389                                                                                      _allObjects.invert((Set<G>) o1),
390                                                                                      _allAttributes
391                                                                                          .invert((Set<M>) o2));
392                                                                                }
393                                                                              };
394  public final AbstractRelation<Set<G>, Set<M>>       upPaths                 = new AbstractRelation<Set<G>, Set<M>>(
395      SetLists.transform(_objects, _allObjects),
396      SetLists.transform(_attributes, _allAttributes),
397      false) {
398
399                                                                                @SuppressWarnings("unchecked")
400                                                                                public boolean
401                                                                                    contains(Object o1, Object o2) {
402                                                                                  return _upPaths.contains(
403                                                                                      _allObjects.invert((Set<G>) o1),
404                                                                                      _allAttributes
405                                                                                          .invert((Set<M>) o2));
406                                                                                }
407                                                                              };
408  public final AbstractRelation<G, M>                 DownPaths               =
409      new AbstractRelation<G, M>(rowHeads, colHeads, false) {
410
411                                                                                    public boolean
412                                                                                        contains(Object o1, Object o2) {
413                                                                                      return _downPaths.contains(
414                                                                                          _objectEquivalence().col(
415                                                                                              rowHeads().indexOf(o1)),
416                                                                                          _attributeEquivalence().col(
417                                                                                              colHeads().indexOf(o2)));
418                                                                                    }
419                                                                                  };
420  public final AbstractRelation<G, M>                 UpPaths                 =
421      new AbstractRelation<G, M>(rowHeads, colHeads, false) {
422
423                                                                                    public boolean
424                                                                                        contains(Object o1, Object o2) {
425                                                                                      return _upPaths.contains(
426                                                                                          _objectEquivalence().col(
427                                                                                              rowHeads().indexOf(o1)),
428                                                                                          _attributeEquivalence().col(
429                                                                                              colHeads().indexOf(o2)));
430                                                                                    }
431                                                                                  };
432  private final Set<M>                                ignoredAttributes       = new HashSet<M>();
433  private final Set<G>                                ignoredObjects          = new HashSet<G>();
434
435  public MatrixContext(final boolean homogen) {
436    super(homogen);
437    initHandlers(true, MatrixContext.AutomaticMode.REDUCE);
438  }
439
440  public MatrixContext(final SetList<G> objects, final SetList<M> attributes, final boolean homogen) {
441    super(objects, attributes, homogen);
442    initHandlers(true, MatrixContext.AutomaticMode.REDUCE);
443  }
444
445  public MatrixContext(
446      final SetList<G> objects,
447      final SetList<M> attributes,
448      final BooleanMatrix matrix,
449      final boolean homogen) {
450    super(objects, attributes, matrix, homogen);
451    initHandlers(true, MatrixContext.AutomaticMode.REDUCE);
452  }
453
454  public MatrixContext(final boolean homogen, final MatrixContext.AutomaticMode automaticMode) {
455    super(homogen);
456    initHandlers(true, automaticMode);
457  }
458
459  public MatrixContext(
460      final SetList<G> objects,
461      final SetList<M> attributes,
462      final boolean homogen,
463      final MatrixContext.AutomaticMode automaticMode) {
464    super(objects, attributes, homogen);
465    initHandlers(true, automaticMode);
466  }
467
468  public MatrixContext(
469      final SetList<G> objects,
470      final SetList<M> attributes,
471      final BooleanMatrix matrix,
472      final boolean homogen,
473      final MatrixContext.AutomaticMode automaticMode) {
474    super(objects, attributes, matrix, homogen);
475    initHandlers(true, automaticMode);
476  }
477
478  private boolean lock = false;
479
480  public final void lock() {
481    lock = true;
482  }
483
484  public final void unlock() {
485    lock = false;
486  }
487
488  public enum AutomaticMode {
489    NONE,
490    CLEAN,
491    REDUCE;
492
493    public final static AutomaticMode fromSize(final int objs, final int atts) {
494      if (objs > 1000 || atts > 1000)
495        return NONE;
496      else if (objs * atts > 10000)
497        return CLEAN;
498      return REDUCE;
499    }
500  }
501
502  public void initHandlers(final boolean selfSelecting, final MatrixContext.AutomaticMode auto) {
503    if (selfSelecting) {
504      addEventHandler(__ -> select(), RelationEvent.SELECTION_CHANGED);
505      select();
506    }
507    switch (auto) {
508    case REDUCE:
509      addEventHandler(__ -> {
510        if (!lock) {
511          reduce();
512        }
513      }, RelationEvent.ALL_CHANGED, RelationEvent.ENTRIES);
514      reduce();
515      break;
516    case CLEAN:
517      addEventHandler(__ -> {
518        if (!lock) {
519          clean();
520        }
521      }, RelationEvent.ALL_CHANGED, RelationEvent.ENTRIES);
522      clean();
523      break;
524    case NONE:
525    default:
526    }
527  }
528
529  public synchronized final void select() {
530    final SetList<M> selectedColHeads = SetLists.difference(colHeads, ignoredAttributes).clone();
531    final SetList<G> selectedRowHeads = SetLists.difference(rowHeads, ignoredObjects).clone();
532    if (ignoredAttributes.isEmpty() && ignoredObjects.isEmpty()) {
533      selection = this;
534      reduce();
535    } else if (selectedColHeads.isEmpty() && selectedRowHeads.isEmpty())
536      selection = new MatrixContext<G, M>(false);
537    else
538      selection = new MatrixContext<G, M>(
539          selectedRowHeads,
540          selectedColHeads,
541          (BooleanMatrix) matrix
542              .selectColumns(Ret.NEW, MatrixContext.this.colHeads().indicesOf(selectedColHeads, false))
543              .selectRows(Ret.NEW, MatrixContext.this.rowHeads().indicesOf(selectedRowHeads, false)),
544          false);
545  }
546
547  public synchronized final void clean() {
548    _objects.clear();
549    _objects.addAll(_objectEquivalence().equivalenceClasses());
550    _attributes.clear();
551    _attributes.addAll(_attributeEquivalence().equivalenceClasses());
552  }
553
554  public synchronized final void reduce() {
555    clean();
556    _downArrows = new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) {
557
558      public final boolean contains(final Object object, final Object attribute) {
559        @SuppressWarnings("unchecked")
560        final int rowIndex = Collections3.firstElement((Set<Integer>) object);
561        @SuppressWarnings("unchecked")
562        final int columnIndex = Collections3.firstElement((Set<Integer>) attribute);
563        if (!matrix.getBoolean(rowIndex, columnIndex))
564          return _col(columnIndex, i).containsAll(
565              _extent(
566                  Collections.singleton(rowIndex),
567                  Collections2.filter(i, Predicates.not(Predicates.equalTo(rowIndex))),
568                  j));
569        return false;
570      }
571    };// .clone();
572    _upArrows = new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) {
573
574      public final boolean contains(final Object object, final Object attribute) {
575        @SuppressWarnings("unchecked")
576        final int _i = Collections3.firstElement((Set<Integer>) object);
577        @SuppressWarnings("unchecked")
578        final int _j = Collections3.firstElement((Set<Integer>) attribute);
579        if (!matrix.getBoolean(_i, _j))
580          return _row(_i, j).containsAll(
581              _intent(Collections.singleton(_j), i, Collections2.filter(j, Predicates.not(Predicates.equalTo(_j)))));
582        return false;
583      }
584    };// .clone();
585    _irreducibleObjects.clear();
586    _irreducibleObjects.addAll(_objects.filter(_isIrreducibleObject));
587    _irreducibleAttributes.clear();
588    _irreducibleAttributes.addAll(_attributes.filter(_isIrreducibleAttribute).clone());
589//    try {
590//      final int rows = _objects.size();
591//      final int cols = _attributes.size();
592//      if (rows == 0 || cols == 0)
593//        return;
594//      @SuppressWarnings("deprecation")
595//      final BooleanMatrix arrowPaths =
596//          BooleanMatrices.transitiveClosure(BooleanMatrices.reflexiveClosure(BooleanMatrices.quadPosition(
597//              BooleanMatrices.empty(rows),
598//              _downArrows.clone().matrix(),
599//              BooleanMatrices.dual(_upArrows.clone().matrix()),
600//              BooleanMatrices.empty(cols))));
601//      final BooleanMatrix downPaths = (BooleanMatrix) arrowPaths.subMatrix(Ret.NEW, 0, rows, rows - 1, rows + cols - 1);
602//      final BooleanMatrix upPaths =
603//          (BooleanMatrix) BooleanMatrices.dual((BooleanMatrix) arrowPaths.subMatrix(
604//              Ret.NEW,
605//              rows,
606//              0,
607//              rows + cols - 1,
608//              rows - 1));
609//      _downPaths = new MatrixRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, downPaths, false);
610//      _upPaths = new MatrixRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, upPaths, false);
611//    } catch (Exception e) {
612//      e.printStackTrace();
613//    }
614  }
615
616  public final Pair<MatrixContext.Incidence, MatrixContext.Incidence>
617      getValue(final G g, final M m, final boolean... withArrows) {
618    if (withArrows.length == 0 || withArrows[0]) {
619      MatrixContext.Incidence first;
620      MatrixContext.Incidence second;
621      boolean down = false;
622      boolean up = false;
623      if (contains(g, m))
624        first = MatrixContext.Incidence.CROSS;
625      else {
626        down = DownArrows.contains(g, m);
627        up = UpArrows.contains(g, m);
628        if (down && up)
629          first = MatrixContext.Incidence.BOTH_ARROW;
630        else if (down)
631          first = MatrixContext.Incidence.DOWN_ARROW;
632        else if (up)
633          first = MatrixContext.Incidence.UP_ARROW;
634        else
635          first = MatrixContext.Incidence.NO_CROSS;
636      }
637      second = null;
638      if (withArrows.length == 0 || (withArrows.length > 1 && withArrows[1])) {
639        final boolean Down = DownPaths.contains(g, m);
640        final boolean Up = UpPaths.contains(g, m);
641        if (up || down)
642          second = null;
643        else if (Down && Up)
644          second = MatrixContext.Incidence.BOTH_PATH;
645        else if (Down)
646          second = MatrixContext.Incidence.DOWN_PATH;
647        else if (Up)
648          second = MatrixContext.Incidence.UP_PATH;
649      }
650      return Pair.of(first, second);
651    } else {
652      if (contains(g, m))
653        return Pair.of(MatrixContext.Incidence.CROSS, null);
654      else
655        return Pair.of(MatrixContext.Incidence.NO_CROSS, null);
656    }
657  }
658
659  public final Iterable<Concept<G, M>> concepts() {
660    return new NextConcept<G, M>(this);
661  }
662
663  public final Relation<G, G> objectQuasiOrder() {
664    return new AbstractRelation<G, G>(rowHeads(), rowHeads(), true) {
665
666      public final boolean contains(final Object object1, final Object object2) {
667        return !matrix
668            .selectRows(Ret.LINK, rowHeads().indexOf(object1))
669            .ge(Ret.LINK, matrix.selectRows(Ret.LINK, rowHeads().indexOf(object2)))
670            .containsBoolean(false);
671      }
672    };
673  }
674
675  public final Relation<M, M> attributeQuasiOrder() {
676    return new AbstractRelation<M, M>(colHeads(), colHeads(), true) {
677
678      public final boolean contains(final Object attribute1, final Object attribute2) {
679        return !matrix
680            .selectColumns(Ret.LINK, colHeads().indexOf(attribute1))
681            .ge(Ret.LINK, matrix.selectColumns(Ret.LINK, colHeads().indexOf(attribute2)))
682            .containsBoolean(false);
683      }
684    };
685  }
686
687  public final Relation<Integer, Integer> _objectEquivalence() {
688    return new AbstractRelation<Integer, Integer>(
689        SetLists.integers(MatrixContext.this.rowHeads().size()),
690        SetLists.integers(MatrixContext.this.rowHeads().size()),
691        true) {
692
693      public final boolean contains(final Object i1, final Object i2) {
694        return matrix.selectRows(Ret.LINK, (Integer) i1).equals(matrix.selectRows(Ret.LINK, (Integer) i2));
695      }
696
697      public final Set<Integer> col(final Object i1) {
698        final Matrix row = matrix.selectRows(Ret.LINK, (Integer) i1);
699        return Stream
700            .iterate(0, Math::incrementExact)
701            .limit(MatrixContext.this.rowHeads.size())
702            .parallel()
703            .filter(i2 -> row.equals(matrix.selectRows(Ret.LINK, i2)))
704            .collect(Collectors.toSet());
705//        return Collections3.fromIterator(
706//            () -> Iterators.filter(
707//                ListIterators.integers(0, MatrixContext.this.rowHeads.size()),
708//                i2 -> row.equals(matrix.selectRows(Ret.LINK, i2))));
709      }
710
711      public final Set<Integer> row(final Object i2) {
712        return col(i2);
713      }
714    };
715  }
716
717  public final Relation<Integer, Integer> _attributeEquivalence() {
718    return new AbstractRelation<Integer, Integer>(
719        SetLists.integers(MatrixContext.this.colHeads().size()),
720        SetLists.integers(MatrixContext.this.colHeads().size()),
721        true) {
722
723      public final boolean contains(final Object j1, final Object j2) {
724        return matrix.selectColumns(Ret.LINK, (Integer) j1).equals(matrix.selectColumns(Ret.LINK, (Integer) j2));
725      }
726
727      public final Set<Integer> col(final Object j1) {
728        final Matrix col = matrix.selectColumns(Ret.LINK, (Integer) j1);
729        return Stream
730            .iterate(0, Math::incrementExact)
731            .limit(MatrixContext.this.colHeads.size())
732            .parallel()
733            .filter(j2 -> col.equals(matrix.selectColumns(Ret.LINK, j2)))
734            .collect(Collectors.toSet());
735//        return Collections3.fromIterator(
736//            () -> Iterators.filter(
737//                ListIterators.integers(0, MatrixContext.this.colHeads.size()),
738//                j2 -> col.equals(matrix.selectColumns(Ret.LINK, j2))));
739      }
740
741      public final Set<Integer> row(final Object i2) {
742        return col(i2);
743      }
744    };
745  }
746
747  public final Set<M> infimumIrreducibles() {
748    return new HashSet<M>(SetLists.transform(_irreducibleAttributes, _firstAttribute));
749  }
750
751  public final Set<G> supremumIrreducibles() {
752    return new HashSet<G>(SetLists.transform(_irreducibleObjects, _firstObject));
753  }
754
755//  public final boolean downArrow(final Object object, final Object attribute) {
756//    return _downArrows.contains(
757//        _objectEquivalence().col(rowHeads().indexOf(object)),
758//        _attributeEquivalence().col(colHeads().indexOf(attribute)));
759//  }
760//
761//  public final boolean upArrow(final Object object, final Object attribute) {
762//    return _upArrows.contains(
763//        _objectEquivalence().col(rowHeads().indexOf(object)),
764//        _attributeEquivalence().col(colHeads().indexOf(attribute)));
765//  }
766//  public final boolean _downArrow(final int rowIndex, final int columnIndex) {
767//    return _downArrows.contains(_objectEquivalence().col(rowIndex), _attributeEquivalence().col(columnIndex));
768//  }
769//
770//  public final boolean _upArrow(final int rowIndex, final int columnIndex) {
771//    return _upArrows.contains(_objectEquivalence().col(rowIndex), _attributeEquivalence().col(columnIndex));
772//  }
773  public final Set<G> extent(final Object... objects) {
774    return extent(Arrays.asList(objects));
775  }
776
777  public final Set<M> intent(final Object... attributes) {
778    return intent(Arrays.asList(attributes));
779  }
780
781  public final Set<G> extent(final Collection<?> objects) {
782    return Collections3
783        .fromIterator(() -> rowHeads.getAll(_extent(rowHeads.indicesOf(objects, true)), true).iterator());
784  }
785
786  public final Set<M> intent(final Collection<?> attributes) {
787    return Collections3
788        .fromIterator(() -> colHeads.getAll(_intent(colHeads.indicesOf(attributes, true)), true).iterator());
789  }
790
791  public final Collection<Integer> _extent(final int... rowIndices) {
792    return _extent(Ints.asList(rowIndices));
793  }
794
795  public final Collection<Integer> _intent(final int... columnIndices) {
796    return _intent(Ints.asList(columnIndices));
797  }
798
799  public final Collection<Integer> _extent(final Iterable<Integer> rowIndices) {
800    return _colAnd(_rowAnd(rowIndices));
801  }
802
803  public final Collection<Integer> _intent(final Iterable<Integer> columnIndices) {
804    return _rowAnd(_colAnd(columnIndices));
805  }
806
807  public final Collection<Integer> _extent(
808      final Collection<Integer> rowIndices,
809      final Collection<Integer> subRowIndices,
810      final Collection<Integer> subColumnIndices) {
811    return _colAnd(_rowAnd(rowIndices, subColumnIndices), subRowIndices);
812  }
813
814  public final Collection<Integer> _intent(
815      final Collection<Integer> columnIndices,
816      final Collection<Integer> subRowIndices,
817      final Collection<Integer> subColumnIndices) {
818    return _rowAnd(_colAnd(columnIndices, subRowIndices), subColumnIndices);
819  }
820
821  public final Concept<G, M> topConcept() {
822    final Set<G> extent = new HashSet<G>(rowHeads());
823    return new Concept<G, M>(extent, rowAnd(extent));
824  }
825
826  public final Concept<G, M> bottomConcept() {
827    final Set<M> intent = new HashSet<M>(colHeads());
828    return new Concept<G, M>(colAnd(intent), intent);
829  }
830
831  public final Concept<G, M> objectConcept(final G g) {
832    final Set<M> intent = new HashSet<M>(row(g));
833    return new Concept<G, M>(colAnd(intent), intent);
834  }
835
836  public final Concept<G, M> attributeConcept(final M m) {
837    final Set<G> extent = new HashSet<G>(col(m));
838    return new Concept<G, M>(extent, rowAnd(extent));
839  }
840
841  public final SetList<G> objectLabels(final Set<G> extent, final Set<M> intent) {
842    return SetLists.filter(SetLists.intersection(rowHeads(), extent), new Predicate<G>() {
843
844      public final boolean apply(final G object) {
845        return row(object).equals(intent);
846      }
847    });
848  }
849
850  public final SetList<M> attributeLabels(final Set<G> extent, final Set<M> intent) {
851    return SetLists.filter(SetLists.intersection(colHeads(), intent), new Predicate<M>() {
852
853      public final boolean apply(final M attribute) {
854        return col(attribute).equals(extent);
855      }
856    });
857  }
858
859  public final void selectAttribute(final M attribute) {
860    ignoredAttributes.remove(attribute);
861    select();
862    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
863  }
864
865  public final void deselectAttribute(final M attribute) {
866    ignoredAttributes.add(attribute);
867    select();
868    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
869  }
870
871  public final void selectAllAttributes() {
872    ignoredAttributes.clear();
873    select();
874    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
875  }
876
877  public final void deselectAllAttributes() {
878    ignoredAttributes.addAll(colHeads());
879    select();
880    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
881  }
882
883  public final Set<M> selectedAttributes() {
884    return Sets.difference(colHeads, ignoredAttributes);
885  }
886
887  public final void selectObject(final G object) {
888    ignoredObjects.remove(object);
889    select();
890    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
891  }
892
893  public final void deselectObject(final G object) {
894    ignoredObjects.add(object);
895    select();
896    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
897  }
898
899  public final void selectAllObjects() {
900    ignoredObjects.clear();
901    select();
902    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
903  }
904
905  public final void deselectAllObjects() {
906    ignoredObjects.addAll(rowHeads());
907    select();
908    push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED));
909  }
910
911  public final Set<G> selectedObjects() {
912    return Sets.difference(rowHeads, ignoredObjects);
913  }
914
915  @Override
916  public final MatrixContext<G, M> getSelection() {
917    return selection;
918  }
919
920  public final Context<G, M> subRelation(final Collection<?> objects, final Collection<?> attributes) {
921    return new AbstractContext<G, M>(
922        SetLists.intersection(rowHeads, objects),
923        SetLists.intersection(colHeads, attributes),
924        false) {
925
926      public final boolean contains(final Object object, final Object attribute) {
927        return rowHeads().contains(object) && colHeads().contains(attribute)
928            && MatrixContext.this.contains(object, attribute);
929      }
930
931      public final MatrixContext<G, M> clone() {
932        // those two lines are wrong, they produce always empty subcontexts, independent of delivered input arguments
933//        if (rowHeads().isEmpty() || colHeads().isEmpty())
934//          return new MatrixContext<G, M>(false);
935        return new MatrixContext<G, M>(
936            rowHeads(),
937            colHeads(),
938            (BooleanMatrix) MatrixContext.this.matrix
939                .selectRows(Ret.NEW, MatrixContext.this.rowHeads().indicesOf(rowHeads(), false))
940                .selectColumns(Ret.NEW, MatrixContext.this.colHeads().indicesOf(colHeads(), false)),
941            false);
942      }
943    };
944  }
945
946  public final Context<M, G> dual() {
947    return new AbstractContext<M, G>(colHeads(), rowHeads(), false) {
948
949      public final boolean contains(final Object attribute, final Object object) {
950        return !MatrixContext.this.contains(object, attribute);
951      }
952
953      public final MatrixContext<M, G> clone() {
954        return new MatrixContext<M, G>(
955            MatrixContext.this.colHeads().clone(),
956            MatrixContext.this.rowHeads().clone(),
957            BooleanMatrices.dual(matrix),
958            false);
959      }
960    };
961  }
962
963  public final Context<G, M> complement() {
964    return new AbstractContext<G, M>(rowHeads(), colHeads(), false) {
965
966      public final boolean contains(final Object object, final Object attribute) {
967        return !MatrixContext.this.contains(object, attribute);
968      }
969
970      public final MatrixContext<G, M> clone() {
971        return new MatrixContext<G, M>(
972            MatrixContext.this.rowHeads().clone(),
973            MatrixContext.this.colHeads().clone(),
974            BooleanMatrices.complement(matrix),
975            false);
976      }
977    };
978  }
979
980  public final Context<M, G> contrary() {
981    return new AbstractContext<M, G>(colHeads(), rowHeads(), false) {
982
983      public final boolean contains(final Object attribute, final Object object) {
984        return !MatrixContext.this.contains(object, attribute);
985      }
986
987      public final MatrixContext<M, G> clone() {
988        return new MatrixContext<M, G>(
989            MatrixContext.this.colHeads().clone(),
990            MatrixContext.this.rowHeads().clone(),
991            BooleanMatrices.complement(BooleanMatrices.dual(matrix)),
992            false);
993      }
994    };
995  }
996
997  public final Relation<Pair<G, M>, Pair<G, M>> ferrersGraph() {
998    final SetList<Pair<G, M>> vertices =
999        SetLists.cartesianProduct(MatrixContext.this.rowHeads(), MatrixContext.this.colHeads()).filter(
1000            new Predicate<Pair<G, M>>() {
1001
1002              public final boolean apply(final Pair<G, M> p) {
1003                return !MatrixContext.this.contains(p.x(), p.y());
1004              }
1005            });
1006    return new AbstractRelation<Pair<G, M>, Pair<G, M>>(vertices, vertices, false) {
1007
1008      public final boolean contains(final Object o1, final Object o2) {
1009        return MatrixContext.this.contains(((Pair<?, ?>) o1).x(), ((Pair<?, ?>) o2).y())
1010            && MatrixContext.this.contains(((Pair<?, ?>) o2).x(), ((Pair<?, ?>) o1).y());
1011      }
1012    };
1013  }
1014
1015  public MatrixContext<G, M> clone() {
1016    return new MatrixContext<G, M>(rowHeads(), colHeads(), BooleanMatrices.clone(matrix), homogen);
1017  }
1018
1019  public String toString() {
1020    if (id.get() == null)
1021      return super.toString();
1022    return id.get();
1023  }
1024
1025  public Set<M> getEquivalenceClass(final M m) {
1026    return new HashSet<M>(attributeQuasiOrder().col(m));
1027  }
1028
1029  public boolean isIrreducible(final M m) {
1030    final int j = colHeads.indexOf(m);
1031    return infimumIrreducibles().contains(m) || _irreducibleAttributes.parallelStream().anyMatch(
1032        // s -> s.parallelStream().map(colHeads.index().inverseToJavaFunction()).anyMatch(n -> n.equals(m)));
1033        s -> s.parallelStream().anyMatch(i -> i.equals(j)));
1034  }
1035
1036//  public Set<M> getReducingAttributes(final M m) {
1037//    if (isIrreducible(m))
1038//      return Collections.singleton(m);
1039//    final Set<M> _m = getEquivalenceClass(m);
1040//  }
1041}