001/*
002 * @author Francesco.Kriegel@gmx.de
003 */
004package conexp.fx.core.collections.relation;
005
006/*
007 * #%L
008 * Concept Explorer FX
009 * %%
010 * Copyright (C) 2010 - 2019 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.AbstractSet;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.HashSet;
032import java.util.Iterator;
033import java.util.List;
034import java.util.ListIterator;
035import java.util.Map;
036import java.util.Set;
037import java.util.concurrent.ConcurrentHashMap;
038import java.util.concurrent.CopyOnWriteArrayList;
039import java.util.stream.Collectors;
040
041import org.ujmp.core.booleanmatrix.BooleanMatrix;
042import org.ujmp.core.booleanmatrix.BooleanMatrix2D;
043import org.ujmp.core.calculation.Calculation.Ret;
044
045import com.google.common.base.Function;
046import com.google.common.base.Predicate;
047import com.google.common.base.Predicates;
048import com.google.common.collect.Collections2;
049import com.google.common.collect.Iterators;
050import com.google.common.collect.Sets;
051import com.google.common.primitives.Ints;
052
053import conexp.fx.core.collections.BitSetFX;
054import conexp.fx.core.collections.Collections3;
055import conexp.fx.core.collections.ListIterators;
056import conexp.fx.core.collections.Pair;
057import conexp.fx.core.collections.setlist.HashSetArrayList;
058import conexp.fx.core.collections.setlist.SetList;
059import conexp.fx.core.collections.setlist.SetLists;
060import conexp.fx.core.math.BooleanMatrices;
061
062public class MatrixRelation<R, C> extends AbstractRelation<R, C> {
063
064  private final class RowHeads extends HashSetArrayList<R> {
065
066    private RowHeads(final Collection<? extends R> c) {
067      super(c);
068    }
069
070    public final boolean add(final R row) {
071      final boolean wasEmpty = isEmpty();
072      if (super.add(row)) {
073        append(wasEmpty, 1);
074        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, row, null));
075        return true;
076      }
077      return false;
078    }
079
080    public final void add(final int i, final R row) {
081      final int size0 = size();
082      super.add(i, row);
083      if (size0 != size()) {
084        insert(i, size0, 1);
085        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, row, null));
086      }
087    }
088
089    public final boolean addAll(final Collection<? extends R> c) {
090      final int size0 = size();
091      @SuppressWarnings("unchecked")
092      final Set<R> changes = new HashSet<R>(Collections3.<R> difference((Collection<R>) c, this));
093      if (super.addAll(c)) {
094        append(size0 == 0, size() - size0);
095        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, changes, null, null));
096        return true;
097      }
098      return false;
099    }
100
101    public final boolean addAll(final int i, final Collection<? extends R> c) {
102      final int size0 = size();
103      @SuppressWarnings("unchecked")
104      final Set<R> changes = new HashSet<R>(Collections3.<R> difference((Collection<R>) c, this));
105      if (super.addAll(i, c)) {
106        insert(i, size0, size() - size0);
107        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, changes, null, null));
108        return true;
109      }
110      return false;
111    }
112
113    @SuppressWarnings("unchecked")
114    @Override
115    public final boolean set(final Object o, final R row) {
116      if (super.set(o, row)) {
117        push(new RelationEvent<R, C>(RelationEvent.ROWS_SET, new Pair<R, R>((R) o, row), null));
118        return true;
119      }
120      return false;
121    }
122
123    @Override
124    public final R set(final int i, final R row) {
125      final R row0 = super.set(i, row);
126      push(new RelationEvent<R, C>(RelationEvent.ROWS_SET, new Pair<R, R>(row0, row), null));
127      return row0;
128    }
129
130    @SuppressWarnings("unchecked")
131    public final boolean remove(final Object o) {
132      final int i = indexOf(o);
133      if (i == -1)
134        return false;
135      super.remove(o);
136      if (isEmpty())
137        matrix = BooleanMatrix2D.Factory.zeros(0, 0);
138      else
139        matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, i);
140      push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, (R) o, null));
141      return true;
142    }
143
144    public final R remove(final int i) {
145      final R row = super.remove(i);
146      if (row != null) {
147        if (isEmpty())
148          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
149        else
150          matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, i);
151        push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, row, null));
152      }
153      return row;
154    }
155
156    public final boolean removeAll(final Collection<?> c) {
157      final Collection<Integer> i = indicesOf(c, false);
158      final Set<R> changes = new HashSet<R>();
159      for (R row : this)
160        if (c.contains(row))
161          changes.add(row);
162      if (super.removeAll(c)) {
163        if (isEmpty())
164          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
165        else
166          matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, i);
167        push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, changes, null, null));
168        return true;
169      }
170      return false;
171    }
172
173    public final boolean retainAll(final Collection<?> c) {
174      final Collection<Integer> i = indicesOf(c, false);
175      final Set<R> changes = new HashSet<R>();
176      for (R row : this)
177        if (!c.contains(row))
178          changes.add(row);
179      if (super.retainAll(c)) {
180        if (isEmpty())
181          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
182        else
183          matrix = (BooleanMatrix) matrix.selectRows(Ret.NEW, i);
184        push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, changes, null, null));
185        return true;
186      }
187      return false;
188    }
189
190    public final ListIterator<R> listIterator(final int i) {
191      return new ListIterator<R>() {
192
193        private final ListIterator<R> it = RowHeads.super.listIterator(i);
194        private R                     pointer;
195        private int                   j;
196
197        public final boolean hasNext() {
198          return it.hasNext();
199        }
200
201        public final int nextIndex() {
202          return it.nextIndex();
203        }
204
205        public final R next() {
206          j = it.nextIndex();
207          pointer = it.next();
208          return pointer;
209        }
210
211        public final boolean hasPrevious() {
212          return it.hasPrevious();
213        }
214
215        public final int previousIndex() {
216          return it.previousIndex();
217        }
218
219        public final R previous() {
220          j = it.previousIndex();
221          pointer = it.previous();
222          return pointer;
223        }
224
225        public final void add(final R row) {
226          pointer = row;
227          final int size0 = size();
228          it.add(row);
229          if (size0 != size()) {
230            insert(++j, size0, 1);
231            push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, row, null));
232          }
233        }
234
235        public final void set(final R row) {
236          pointer = row;
237          it.set(row);
238        }
239
240        public final void remove() {
241          it.remove();
242          if (isEmpty())
243            matrix = BooleanMatrix2D.Factory.zeros(0, 0);
244          else
245            matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, j);
246          push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, pointer, null));
247        }
248      };
249    }
250
251    public final void clear() {
252      super.clear();
253      matrix = BooleanMatrix2D.Factory.zeros(0, 0);
254      push(new RelationEvent<R, C>(RelationEvent.ROWS_CLEARED));
255    }
256
257    private final void append(final boolean wasEmpty, final int rows) {
258      if (colHeads == null)
259        return;
260      final BooleanMatrix zeros = BooleanMatrix2D.Factory.zeros(rows, colHeads.size());
261      if (wasEmpty)
262        matrix = zeros;
263      else
264        matrix = (BooleanMatrix) matrix.appendVertically(Ret.NEW, zeros);
265    }
266
267    private final void insert(final int i, final int size0, final int rows) {
268      if (colHeads == null)
269        return;
270      final int cols = colHeads.size();
271      final BooleanMatrix zeros = BooleanMatrix2D.Factory.zeros(rows, cols);
272      if (size0 == 0)
273        matrix = zeros;
274      else if (i == 0)
275        matrix = (BooleanMatrix) zeros.appendVertically(Ret.NEW, matrix);
276      else if (i == size0)
277        matrix = (BooleanMatrix) matrix.appendVertically(Ret.NEW, zeros);
278      else {
279        BooleanMatrix upper = matrix.subMatrix(Ret.LINK, 0, 0, i - 1, cols - 1).toBooleanMatrix();
280        BooleanMatrix lower = matrix.subMatrix(Ret.LINK, i, 0, size0 - 1, cols - 1).toBooleanMatrix();
281        matrix = (BooleanMatrix) upper.appendVertically(Ret.NEW, zeros).appendVertically(Ret.NEW, lower);
282      }
283    }
284  }
285
286  private final class ColHeads extends HashSetArrayList<C> {
287
288    private ColHeads(final Collection<? extends C> c) {
289      super(c);
290    }
291
292    public final boolean add(final C col) {
293      final boolean wasEmpty = isEmpty();
294      if (super.add(col)) {
295        append(wasEmpty, 1);
296        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, col));
297        return true;
298      }
299      return false;
300    }
301
302    public final void add(final int i, final C col) {
303      final int size0 = size();
304      super.add(i, col);
305      if (size0 != size()) {
306        insert(i, size0, 1);
307        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, col));
308      }
309    }
310
311    public final boolean addAll(final Collection<? extends C> c) {
312      final int size0 = size();
313      @SuppressWarnings("unchecked")
314      final Set<C> changes = new HashSet<C>(Collections3.<C> difference((Collection<C>) c, this));
315      if (super.addAll(c)) {
316        append(size0 == 0, size() - size0);
317        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, changes, null));
318        return true;
319      }
320      return false;
321    }
322
323    public final boolean addAll(final int i, final Collection<? extends C> c) {
324      final int size0 = size();
325      @SuppressWarnings("unchecked")
326      final Set<C> changes = new HashSet<C>(Collections3.<C> difference((Collection<C>) c, this));
327      if (super.addAll(i, c)) {
328        insert(i, size0, size() - size0);
329        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, changes, null));
330        return true;
331      }
332      return false;
333    }
334
335    @SuppressWarnings("unchecked")
336    @Override
337    public final boolean set(final Object o, final C col) {
338      if (super.set(o, col)) {
339        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_SET, null, new Pair<C, C>((C) o, col)));
340        return true;
341      }
342      return false;
343    }
344
345    @Override
346    public final C set(final int i, final C col) {
347      final C col0 = super.set(i, col);
348      push(new RelationEvent<R, C>(RelationEvent.COLUMNS_SET, null, new Pair<C, C>(col0, col)));
349      return col0;
350    }
351
352    @SuppressWarnings("unchecked")
353    public final boolean remove(final Object o) {
354      final int i = indexOf(o);
355      if (i == -1)
356        return false;
357      super.remove(o);
358      if (isEmpty())
359        matrix = BooleanMatrix2D.Factory.zeros(0, 0);
360      else
361        matrix = (BooleanMatrix) matrix.deleteColumns(Ret.NEW, i);
362      push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, (C) o));
363      return true;
364    }
365
366    public final C remove(final int i) {
367      final C col = super.remove(i);
368      if (col != null) {
369        if (isEmpty())
370          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
371        else
372          matrix = (BooleanMatrix) matrix.deleteColumns(Ret.NEW, i);
373        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, col));
374      }
375      return col;
376    }
377
378    public final boolean removeAll(final Collection<?> c) {
379      final Collection<Integer> i = indicesOf(c, false);
380      final Set<C> changes = new HashSet<C>();
381      for (C col : this)
382        if (c.contains(col))
383          changes.add(col);
384      if (super.removeAll(c)) {
385        if (isEmpty())
386          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
387        else
388          matrix = (BooleanMatrix) matrix.deleteColumns(Ret.NEW, i);
389        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, changes, null));
390        return true;
391      }
392      return false;
393    }
394
395    public final boolean retainAll(final Collection<?> c) {
396      final Collection<Integer> i = indicesOf(c, false);
397      final Set<C> changes = new HashSet<C>();
398      for (C col : this)
399        if (!c.contains(col))
400          changes.add(col);
401      if (super.retainAll(c)) {
402        if (isEmpty())
403          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
404        else
405          matrix = (BooleanMatrix) matrix.selectColumns(Ret.NEW, i);
406        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, changes, null));
407        return true;
408      }
409      return false;
410    }
411
412    public final ListIterator<C> listIterator(final int i) {
413      return new ListIterator<C>() {
414
415        private final ListIterator<C> it = ColHeads.super.listIterator(i);
416        private C                     pointer;
417        private int                   j;
418
419        public final boolean hasNext() {
420          return it.hasNext();
421        }
422
423        public final C next() {
424          j = it.nextIndex();
425          pointer = it.next();
426          return pointer;
427        }
428
429        public final boolean hasPrevious() {
430          return it.hasPrevious();
431        }
432
433        public final C previous() {
434          j = it.previousIndex();
435          pointer = it.previous();
436          return pointer;
437        }
438
439        public final int nextIndex() {
440          return it.nextIndex();
441        }
442
443        public final int previousIndex() {
444          return it.previousIndex();
445        }
446
447        public final void remove() {
448          it.remove();
449          if (isEmpty())
450            matrix = BooleanMatrix2D.Factory.zeros(0, 0);
451          else
452            matrix = (BooleanMatrix) matrix.deleteColumns(Ret.NEW, j);
453          push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, pointer));
454        }
455
456        public final void set(final C col) {
457          pointer = col;
458          it.set(col);
459        }
460
461        public final void add(final C col) {
462          pointer = col;
463          final int size0 = size();
464          it.add(col);
465          if (size0 != size()) {
466            insert(++j, size0, 1);
467            push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, col));
468          }
469        }
470      };
471    }
472
473    public final void clear() {
474      super.clear();
475      matrix = BooleanMatrix2D.Factory.zeros(0, 0);
476      push(new RelationEvent<R, C>(RelationEvent.COLUMNS_CLEARED));
477    }
478
479    private final void append(final boolean wasEmpty, final int cols) {
480      final BooleanMatrix zeros = BooleanMatrix2D.Factory.zeros(rowHeads.size(), cols);
481      if (wasEmpty)
482        matrix = zeros;
483      else
484        matrix = (BooleanMatrix) matrix.appendHorizontally(Ret.NEW, zeros);
485    }
486
487    private final void insert(final int i, final int size0, final int cols) {
488      final int rows = rowHeads.size();
489      final BooleanMatrix zeros = BooleanMatrix2D.Factory.zeros(rows, cols);
490      if (size0 == 0)
491        matrix = zeros;
492      else if (i == 0)
493        matrix = (BooleanMatrix) zeros.appendHorizontally(Ret.NEW, matrix);
494      else if (i == size0)
495        matrix = (BooleanMatrix) matrix.appendHorizontally(Ret.NEW, zeros);
496      else {
497        final BooleanMatrix left = matrix.subMatrix(Ret.LINK, 0, 0, rows - 1, i - 1).toBooleanMatrix();
498        final BooleanMatrix right = matrix.subMatrix(Ret.LINK, 0, i, rows - 1, size0 - 1).toBooleanMatrix();
499        matrix = (BooleanMatrix) left.appendHorizontally(Ret.NEW, zeros).appendHorizontally(Ret.NEW, right);
500      }
501    }
502  }
503
504  private final class Heads extends HashSetArrayList<R> {
505
506    private Heads(final Collection<? extends R> c) {
507      super(c);
508    }
509
510    @SuppressWarnings("unchecked")
511    public final boolean add(final R head) {
512      if (super.add(head)) {
513        append(size() - 1, 1);
514        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, head, null));
515        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, (C) head));
516        return true;
517      }
518      return false;
519    }
520
521    @SuppressWarnings("unchecked")
522    public final void add(final int i, final R head) {
523      final int size0 = size();
524      super.add(i, head);
525      if (size0 != size()) {
526        insert(i, size0, 1);
527        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, head, null));
528        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, (C) head));
529      }
530    }
531
532    @SuppressWarnings("unchecked")
533    public final boolean addAll(final Collection<? extends R> c) {
534      final int size0 = size();
535      final Set<R> changes = new HashSet<R>(Collections3.<R> difference((Collection<R>) c, this));
536      if (super.addAll(c)) {
537        append(size0, size() - size0);
538        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, changes, null, null));
539        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, (Set<C>) changes, null));
540        return true;
541      }
542      return false;
543    }
544
545    @SuppressWarnings("unchecked")
546    public final boolean addAll(final int i, final Collection<? extends R> c) {
547      final int size0 = size();
548      final Set<R> changes = new HashSet<R>(Collections3.<R> difference((Collection<R>) c, this));
549      if (super.addAll(i, c)) {
550        insert(i, size0, size() - size0);
551        push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, changes, null, null));
552        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, (Set<C>) changes, null));
553        return true;
554      }
555      return false;
556    }
557
558    @SuppressWarnings("unchecked")
559    @Override
560    public final boolean set(final Object o, final R head) {
561      if (super.set(o, head)) {
562        push(new RelationEvent<R, C>(RelationEvent.ROWS_SET, new Pair<R, R>((R) o, head), null));
563        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_SET, null, new Pair<C, C>((C) o, (C) head)));
564        return true;
565      }
566      return false;
567    }
568
569    @SuppressWarnings("unchecked")
570    @Override
571    public final R set(final int i, final R head) {
572      final R head0 = super.set(i, head);
573      push(new RelationEvent<R, C>(RelationEvent.ROWS_SET, new Pair<R, R>(head0, head), null));
574      push(new RelationEvent<R, C>(RelationEvent.COLUMNS_SET, null, new Pair<C, C>((C) head0, (C) head)));
575      return head0;
576    }
577
578    @SuppressWarnings("unchecked")
579    public final boolean remove(final Object o) {
580      final int i = indexOf(o);
581      if (i == -1)
582        return false;
583      super.remove(o);
584      if (isEmpty())
585        matrix = BooleanMatrix2D.Factory.zeros(0, 0);
586      else
587        matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, i).deleteColumns(Ret.NEW, i);
588      push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, (R) o, null));
589      push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, (C) o));
590      return true;
591    }
592
593    @SuppressWarnings("unchecked")
594    public final R remove(final int i) {
595      final R head = super.remove(i);
596      if (head != null) {
597        if (isEmpty())
598          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
599        else
600          matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, i).deleteColumns(Ret.NEW, i);
601        push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, head, null));
602        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, (C) head));
603      }
604      return head;
605    }
606
607    @SuppressWarnings("unchecked")
608    public final boolean removeAll(final Collection<?> c) {
609      final Collection<Integer> i = Sets.newHashSet(indicesOf(c, false));
610      final Set<R> changes = new HashSet<R>();
611      for (R head : this)
612        if (c.contains(head))
613          changes.add(head);
614      if (super.removeAll(c)) {
615        if (isEmpty())
616          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
617        else
618          matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, i).deleteColumns(Ret.NEW, i);
619        push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, changes, null, null));
620        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, (Set<C>) changes, null));
621        return true;
622      }
623      return false;
624    }
625
626    @SuppressWarnings("unchecked")
627    public final boolean retainAll(final Collection<?> c) {
628      final Collection<Integer> i = indicesOf(c, false);
629      final Set<R> changes = new HashSet<R>();
630      for (R head : this)
631        if (c.contains(head))
632          changes.add(head);
633      if (super.removeAll(c)) {
634        if (isEmpty())
635          matrix = BooleanMatrix2D.Factory.zeros(0, 0);
636        else
637          matrix = (BooleanMatrix) matrix.selectRows(Ret.NEW, i).selectColumns(Ret.NEW, i);
638        push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, changes, null, null));
639        push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, (Set<C>) changes, null));
640        return true;
641      }
642      return false;
643    }
644
645    public final ListIterator<R> listIterator(final int i) {
646      return new ListIterator<R>() {
647
648        private final ListIterator<R> it = Heads.super.listIterator(i);
649        private R                     pointer;
650        private int                   j;
651
652        public final boolean hasNext() {
653          return it.hasNext();
654        }
655
656        public final int nextIndex() {
657          return it.nextIndex();
658        }
659
660        public final R next() {
661          j = it.nextIndex();
662          pointer = it.next();
663          return pointer;
664        }
665
666        public final boolean hasPrevious() {
667          return it.hasPrevious();
668        }
669
670        public final int previousIndex() {
671          return it.previousIndex();
672        }
673
674        public final R previous() {
675          j = it.previousIndex();
676          pointer = it.previous();
677          return pointer;
678        }
679
680        @SuppressWarnings("unchecked")
681        public final void add(final R head) {
682          pointer = head;
683          final int size0 = size();
684          it.add(head);
685          if (size0 != size()) {
686            j++;
687            insert(j, size0, 1);
688            push(new RelationEvent<R, C>(RelationEvent.ROWS_ADDED, head, null));
689            push(new RelationEvent<R, C>(RelationEvent.COLUMNS_ADDED, null, (C) head));
690          }
691        }
692
693        public final void set(final R head) {
694          pointer = head;
695          it.set(head);
696        }
697
698        @SuppressWarnings("unchecked")
699        public final void remove() {
700          it.remove();
701          if (isEmpty())
702            matrix = BooleanMatrix2D.Factory.zeros(0, 0);
703          else
704            matrix = (BooleanMatrix) matrix.deleteRows(Ret.NEW, j).deleteColumns(Ret.NEW, j);
705          push(new RelationEvent<R, C>(RelationEvent.ROWS_REMOVED, pointer, null));
706          push(new RelationEvent<R, C>(RelationEvent.COLUMNS_REMOVED, null, (C) pointer));
707        }
708      };
709    }
710
711    public final void clear() {
712      super.clear();
713      matrix = BooleanMatrix2D.Factory.zeros(0, 0);
714      push(new RelationEvent<R, C>(RelationEvent.ROWS_CLEARED));
715      push(new RelationEvent<R, C>(RelationEvent.COLUMNS_CLEARED));
716    }
717
718    private final void append(final int size0, final int heads) {
719      if (size0 == 0) {
720        matrix = BooleanMatrix2D.Factory.zeros(heads, heads);
721        return;
722      }
723      final BooleanMatrix rows = BooleanMatrix2D.Factory.zeros(heads, size0);
724      final BooleanMatrix cols = BooleanMatrix2D.Factory.zeros(size0 + heads, heads);
725      matrix = (BooleanMatrix) matrix.appendVertically(Ret.NEW, rows).appendHorizontally(Ret.NEW, cols);
726    }
727
728    private final void insert(final int i, final int size0, final int heads) {
729      if (size0 == 0) {
730        matrix = BooleanMatrix2D.Factory.zeros(heads, heads);
731        return;
732      }
733      final BooleanMatrix rows = BooleanMatrix2D.Factory.zeros(heads, size0);
734      final BooleanMatrix cols = BooleanMatrix2D.Factory.zeros(size0 + heads, heads);
735      if (i == 0)
736        matrix = (BooleanMatrix) cols.appendHorizontally(Ret.NEW, rows.appendVertically(Ret.NEW, matrix));
737      else if (i == size0)
738        matrix = (BooleanMatrix) matrix.appendVertically(Ret.NEW, rows).appendHorizontally(Ret.NEW, cols);
739      else {
740        final BooleanMatrix upper = (BooleanMatrix) matrix.subMatrix(Ret.LINK, 0, 0, i - 1, size0 - 1);
741        final BooleanMatrix lower = (BooleanMatrix) matrix.subMatrix(Ret.LINK, i, 0, size0 - 1, size0 - 1);
742        matrix = (BooleanMatrix) upper.appendVertically(Ret.NEW, rows).appendVertically(Ret.NEW, lower);
743        final BooleanMatrix left = (BooleanMatrix) matrix.subMatrix(Ret.LINK, 0, 0, size0 + heads - 1, i - 1);
744        final BooleanMatrix right = (BooleanMatrix) matrix.subMatrix(Ret.LINK, 0, i, size0 + heads - 1, size0 - 1);
745        matrix = (BooleanMatrix) left.appendHorizontally(Ret.NEW, cols).appendHorizontally(Ret.NEW, right);
746      }
747    }
748  }
749
750  protected BooleanMatrix                                                 matrix;
751  private final Map<RelationEvent.Type, List<RelationEventHandler<R, C>>> eventHandlers = new ConcurrentHashMap<>();
752
753  public MatrixRelation(final boolean homogen) {
754    this(SetLists.<R> empty(), SetLists.<C> empty(), BooleanMatrix2D.Factory.zeros(0, 0), homogen);
755  }
756
757  public MatrixRelation(final SetList<R> rowHeads, final SetList<C> colHeads, final boolean homogen) {
758    this(rowHeads, colHeads, BooleanMatrix2D.Factory.zeros(rowHeads.size(), colHeads.size()), homogen);
759  }
760
761  @SuppressWarnings("unchecked")
762  public MatrixRelation(
763      final SetList<R> rowHeads,
764      final SetList<C> colHeads,
765      final BooleanMatrix matrix,
766      final boolean homogen) {
767    super(homogen);
768    if (homogen) {
769      if (!rowHeads.equals(colHeads))
770        throw new NoHomogenRelationException();
771      this.rowHeads = new Heads(rowHeads);
772      this.colHeads = (SetList<C>) this.rowHeads;
773    } else {
774      this.rowHeads = new RowHeads(rowHeads);
775      this.colHeads = new ColHeads(colHeads);
776    }
777    this.matrix = matrix;
778  }
779
780  public final boolean add(final R row, final C col) {
781    boolean changed;
782    final int i;
783    if (rowHeads.add(row)) {
784      i = rowHeads.size() - 1;
785      changed = true;
786    } else {
787      i = rowHeads.indexOf(row);
788      changed = false;
789    }
790    final int j;
791    if (colHeads.add(col)) {
792      j = colHeads.size() - 1;
793      changed = true;
794    } else
795      j = colHeads.indexOf(col);
796    if (changed || !matrix.getBoolean(i, j)) {
797      matrix.setBoolean(true, i, j);
798      push(
799          new RelationEvent<R, C>(
800              RelationEvent.ENTRIES_ADDED,
801              null,
802              null,
803              Collections.singleton(new Pair<R, C>(row, col))));
804      return true;
805    }
806    return false;
807  }
808
809  @SuppressWarnings("unchecked")
810  public boolean addFast(final Object o1, final Object o2) {
811    final int i = rowHeads.indexOf(o1);
812//    if (i != -1) {
813    final int j = colHeads.indexOf(o2);
814//      if (j != -1)
815    if (!matrix.getBoolean(i, j)) {
816      matrix.setBoolean(true, i, j);
817      push(
818          new RelationEvent<R, C>(
819              RelationEvent.ENTRIES_ADDED,
820              null,
821              null,
822              Collections.singleton(new Pair<R, C>((R) o1, (C) o2))));
823      return true;
824    }
825//    }
826    return false;
827  }
828
829//  @SuppressWarnings("unchecked")
830  public void addFastSilent(final Object o1, final Object o2) {
831    final int i = rowHeads.indexOf(o1);
832//    if (i != -1) {
833    final int j = colHeads.indexOf(o2);
834//      if (j != -1)
835//    if (!matrix.getBoolean(i, j)) {
836    matrix.setBoolean(true, i, j);
837//      push(new RelationEvent<R, C>(RelationEvent.ENTRIES_ADDED, null, null, Collections.singleton(new Pair<R, C>(
838//          (R) o1,
839//          (C) o2))));
840//      return true;
841//    }
842//    }
843//    return false;
844  }
845
846  @SuppressWarnings("unchecked")
847  public final boolean addAll(final Relation<? extends R, ? extends C> r) {
848    boolean changed = false;
849    if (r instanceof MatrixRelation) {
850      final MatrixRelation<? extends R, ? extends C> _r = (MatrixRelation<? extends R, ? extends C>) r;
851      rowHeads.addAll(_r.rowHeads);
852      colHeads.addAll(_r.colHeads);
853      matrix
854          .selectRows(Ret.LINK, rowHeads.indicesOf(_r.rowHeads, true))
855          .selectColumns(Ret.LINK, colHeads.indicesOf(_r.colHeads, true))
856          .or(Ret.ORIG, _r.matrix);
857      changed = true;
858      push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED, null, null, null));
859    } else {
860      final Iterator<?> it = r.iterator();
861      Pair<? extends R, ? extends C> next;
862      while (it.hasNext()) {
863        next = (Pair<? extends R, ? extends C>) it.next();
864        changed |= add(next.x(), next.y());
865      }
866    }
867    return changed;
868  }
869
870  @SuppressWarnings("unchecked")
871  public final boolean addAllFast(final Relation<?, ?> r) {
872    boolean changed = false;
873    if (r instanceof MatrixRelation) {
874      final MatrixRelation<?, ?> _r = (MatrixRelation<?, ?>) r;
875      matrix
876          .selectRows(Ret.LINK, rowHeads.indicesOf(_r.rowHeads, false))
877          .selectColumns(Ret.LINK, colHeads.indicesOf(_r.colHeads, false))
878          .or(
879              Ret.ORIG,
880              _r.matrix.selectRows(Ret.LINK, _r.rowHeads.indicesOf(rowHeads, false)).selectColumns(
881                  Ret.LINK,
882                  _r.colHeads.indicesOf(colHeads, false)));
883      push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED, null, null, null));
884      changed = true;
885    } else {
886      final Iterator<?> it = r.iterator();
887      Pair<? extends R, ? extends C> next;
888      while (it.hasNext()) {
889        next = (Pair<? extends R, ? extends C>) it.next();
890        changed |= addFast(next.x(), next.y());
891      }
892    }
893    return changed;
894  }
895
896  @SuppressWarnings("unchecked")
897  public boolean remove(final Object o1, final Object o2) {
898    final int i = rowHeads.indexOf(o1);
899    if (i != -1) {
900      final int j = colHeads.indexOf(o2);
901      if (j != -1 && matrix.getBoolean(i, j)) {
902        matrix.setBoolean(false, i, j);
903        push(
904            new RelationEvent<R, C>(
905                RelationEvent.ENTRIES_REMOVED,
906                null,
907                null,
908                Collections.singleton(new Pair<R, C>((R) o1, (C) o2))));
909        return true;
910      }
911    }
912    return false;
913  }
914
915  @SuppressWarnings("unchecked")
916  public final boolean removeAll(final Relation<?, ?> r) {
917    boolean changed = false;
918    if (r instanceof MatrixRelation) {
919      final MatrixRelation<?, ?> _r = (MatrixRelation<?, ?>) r;
920      matrix
921          .selectRows(Ret.LINK, rowHeads.indicesOf(_r.rowHeads, false))
922          .selectColumns(Ret.LINK, colHeads.indicesOf(_r.colHeads, false))
923          .and(
924              Ret.ORIG,
925              _r.matrix
926                  .selectRows(Ret.LINK, _r.rowHeads.indicesOf(rowHeads, false))
927                  .selectColumns(Ret.LINK, _r.colHeads.indicesOf(colHeads, false))
928                  .not(Ret.LINK));
929      push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED, null, null, null));
930      changed = true;
931    } else {
932      final Iterator<?> it = r.iterator();
933      Pair<? extends R, ? extends C> next;
934      while (it.hasNext()) {
935        next = (Pair<? extends R, ? extends C>) it.next();
936        changed |= remove(next.x(), next.y());
937      }
938    }
939    return changed;
940  }
941
942  public final boolean retainAll(final Relation<?, ?> r) {
943    boolean changed = false;
944    if (r instanceof MatrixRelation) {
945      final MatrixRelation<?, ?> _r = (MatrixRelation<?, ?>) r;
946      final Collection<Integer> i = rowHeads.indicesOf(_r.rowHeads, false);
947      final Collection<Integer> j = colHeads.indicesOf(_r.colHeads, false);
948      final Collection<Integer> i0 =
949          Collections2.filter(SetLists.integers(rowHeads.size()), Predicates.not(Predicates.in(i)));
950      final Collection<Integer> j0 =
951          Collections2.filter(SetLists.integers(colHeads.size()), Predicates.not(Predicates.in(j)));
952      matrix.selectRows(Ret.LINK, i0).selectColumns(Ret.LINK, j0).and(Ret.ORIG, false);
953      matrix.selectRows(Ret.LINK, i0).selectColumns(Ret.LINK, j).and(Ret.ORIG, false);
954      matrix.selectRows(Ret.LINK, i).selectColumns(Ret.LINK, j0).and(Ret.ORIG, false);
955      matrix.selectRows(Ret.LINK, i).selectColumns(Ret.LINK, j).and(
956          Ret.ORIG,
957          _r.matrix.selectRows(Ret.LINK, _r.rowHeads.indicesOf(rowHeads, false)).selectColumns(
958              Ret.LINK,
959              _r.colHeads.indicesOf(colHeads, false)));
960      push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED, null, null, null));
961      changed = true;
962    } else {
963      for (C col : colHeads) {
964        final Set<R> column = col(col);
965        if (r.colHeads().contains(col))
966          changed |= column.retainAll(r.col(col));
967        else {
968          changed |= !column.isEmpty();
969          column.clear();
970        }
971      }
972    }
973    return changed;
974  }
975
976  public final boolean contains(final Object o1, final Object o2) {
977    final int i = rowHeads.indexOf(o1);
978    if (i == -1)
979      return false;
980    final int j = colHeads.indexOf(o2);
981    return j != -1 && matrix.getBoolean(i, j);
982  }
983
984  public final boolean containsAll(final Relation<?, ?> r) {
985    if (rowHeads.containsAll(r.rowHeads()) && colHeads.containsAll(r.colHeads())) {
986      if (r instanceof MatrixRelation) {
987        final MatrixRelation<?, ?> _r = (MatrixRelation<?, ?>) r;
988        return matrix
989            .selectRows(Ret.LINK, rowHeads.indicesOf(_r.rowHeads, true))
990            .selectColumns(Ret.LINK, colHeads.indicesOf(_r.colHeads, true))
991            .equals(_r.matrix);
992      } else {
993        final Iterator<?> iterator = r.iterator();
994        Pair<?, ?> next;
995        while (iterator.hasNext()) {
996          next = (Pair<?, ?>) iterator.next();
997          if (!contains(next.x(), next.y()))
998            return false;
999        }
1000        return true;
1001      }
1002    }
1003    return false;
1004  }
1005
1006  public final Set<C> row(final Object o) {
1007    return new AbstractSet<C>() {
1008
1009      private final int i = rowHeads.indexOf(o);
1010
1011      @SuppressWarnings("unchecked")
1012      public final boolean add(final C col) {
1013        final int j = colHeads.indexOf(col);
1014        if (matrix.getBoolean(i, j))
1015          return false;
1016        matrix.setBoolean(true, i, j);
1017        push(
1018            new RelationEvent<R, C>(
1019                RelationEvent.ENTRIES_ADDED,
1020                null,
1021                null,
1022                Collections.singleton(new Pair<R, C>((R) o, col))));
1023        return true;
1024      }
1025
1026      public final boolean addAll(final Collection<? extends C> c) {
1027        boolean changed = false;
1028        @SuppressWarnings("unchecked")
1029        final Set<C> changes = new HashSet<C>(Collections3.<C> difference((Collection<C>) c, this));
1030        for (C col : c) {
1031          final int j = colHeads.indexOf(col);
1032          if (!matrix.getBoolean(i, j)) {
1033            matrix.setBoolean(true, i, j);
1034            changed = true;
1035            push(
1036                new RelationEvent<R, C>(
1037                    RelationEvent.ENTRIES_ADDED,
1038                    null,
1039                    null,
1040                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<C, Pair<R, C>>() {
1041
1042                      @SuppressWarnings("unchecked")
1043                      public final Pair<R, C> apply(final C col) {
1044                        return new Pair<R, C>((R) o, col);
1045                      }
1046                    }))));
1047          }
1048        }
1049        return changed;
1050      }
1051
1052      public final boolean contains(final Object o) {
1053        return matrix.getBoolean(i, colHeads.indexOf(o));
1054      }
1055
1056      @SuppressWarnings("unchecked")
1057      public final boolean remove(final Object o2) {
1058        final int j = colHeads.indexOf(o2);
1059        if (!matrix.getBoolean(i, j))
1060          return false;
1061        matrix.setBoolean(false, i, j);
1062        push(
1063            new RelationEvent<R, C>(
1064                RelationEvent.ENTRIES_REMOVED,
1065                null,
1066                null,
1067                Collections.singleton(new Pair<R, C>((R) o, (C) o2))));
1068        return true;
1069      }
1070
1071      public final boolean removeAll(final Collection<?> c) {
1072        boolean changed = false;
1073        final Set<C> changes = new HashSet<C>();
1074        for (C col : this)
1075          if (c.contains(col))
1076            changes.add(col);
1077        for (Object o2 : c) {
1078          final int j = colHeads.indexOf(o2);
1079          if (matrix.getBoolean(i, j)) {
1080            matrix.setBoolean(false, i, j);
1081            changed = true;
1082            push(
1083                new RelationEvent<R, C>(
1084                    RelationEvent.ENTRIES_REMOVED,
1085                    null,
1086                    null,
1087                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<C, Pair<R, C>>() {
1088
1089                      @SuppressWarnings("unchecked")
1090                      public final Pair<R, C> apply(final C col) {
1091                        return new Pair<R, C>((R) o, col);
1092                      }
1093                    }))));
1094          }
1095        }
1096        return changed;
1097      }
1098
1099      public final boolean retainAll(final Collection<?> c) {
1100        boolean changed = false;
1101        final Set<C> changes = new HashSet<C>();
1102        for (C col : this)
1103          if (!c.contains(col))
1104            changes.add(col);
1105        for (Object o2 : Collections2.filter(colHeads, Predicates.not(Predicates.in(c)))) {
1106          final int j = colHeads.indexOf(o2);
1107          if (matrix.getBoolean(i, j)) {
1108            matrix.setBoolean(false, i, j);
1109            changed = true;
1110            push(
1111                new RelationEvent<R, C>(
1112                    RelationEvent.ENTRIES_REMOVED,
1113                    null,
1114                    null,
1115                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<C, Pair<R, C>>() {
1116
1117                      @SuppressWarnings("unchecked")
1118                      public final Pair<R, C> apply(final C col) {
1119                        return new Pair<R, C>((R) o, col);
1120                      }
1121                    }))));
1122          }
1123        }
1124        return changed;
1125      }
1126
1127      public final void clear() {
1128        for (int j = 0; j < colHeads.size(); j++)
1129          matrix.setBoolean(false, i, j);
1130      }
1131
1132      public final Iterator<C> iterator() {
1133        return Iterators
1134            .transform(Iterators.filter(ListIterators.integers(0, colHeads.size()), new Predicate<Integer>() {
1135
1136              public final boolean apply(final Integer j) {
1137                return matrix.getBoolean(i, j);
1138              }
1139            }), new Function<Integer, C>() {
1140
1141              public final C apply(final Integer j) {
1142                return colHeads.get(j);
1143              }
1144            });
1145      }
1146
1147      public final int size() {
1148        int size = 0;
1149        if (i != -1)
1150          for (int j = 0; j < colHeads.size(); j++)
1151            if (matrix.getBoolean(i, j))
1152              size++;
1153        return size;
1154      }
1155
1156      public final HashSet<C> clone() {
1157        return new HashSet<C>(this);
1158      }
1159    };
1160  }
1161
1162  public final Set<R> col(final Object o) {
1163    return new AbstractSet<R>() {
1164
1165      private final int j = colHeads.indexOf(o);
1166
1167      @SuppressWarnings("unchecked")
1168      public final boolean add(final R row) {
1169        final int i = rowHeads.indexOf(row);
1170        if (matrix.getBoolean(i, j))
1171          return false;
1172        matrix.setBoolean(true, i, j);
1173        push(
1174            new RelationEvent<R, C>(
1175                RelationEvent.ENTRIES_ADDED,
1176                null,
1177                null,
1178                Collections.singleton(new Pair<R, C>(row, (C) o))));
1179        return true;
1180      };
1181
1182      public final boolean addAll(Collection<? extends R> c) {
1183        boolean changed = false;
1184        @SuppressWarnings("unchecked")
1185        final Set<R> changes = new HashSet<R>(Collections3.<R> difference((Collection<R>) c, this));
1186        for (R row : c) {
1187          final int i = rowHeads.indexOf(row);
1188          if (!matrix.getBoolean(i, j)) {
1189            matrix.setBoolean(true, i, j);
1190            changed = true;
1191            push(
1192                new RelationEvent<R, C>(
1193                    RelationEvent.ENTRIES_ADDED,
1194                    null,
1195                    null,
1196                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<R, Pair<R, C>>() {
1197
1198                      @SuppressWarnings("unchecked")
1199                      public final Pair<R, C> apply(final R row) {
1200                        return new Pair<R, C>(row, (C) o);
1201                      }
1202                    }))));
1203          }
1204        }
1205        return changed;
1206      }
1207
1208      public final boolean contains(final Object o1) {
1209        return matrix.getBoolean(rowHeads.indexOf(o1), j);
1210      }
1211
1212      @SuppressWarnings("unchecked")
1213      public final boolean remove(final Object o1) {
1214        final int i = rowHeads.indexOf(o1);
1215        if (!matrix.getBoolean(i, j))
1216          return false;
1217        matrix.setBoolean(false, i, j);
1218        push(
1219            new RelationEvent<R, C>(
1220                RelationEvent.ENTRIES_REMOVED,
1221                null,
1222                null,
1223                Collections.singleton(new Pair<R, C>((R) o1, (C) o))));
1224        return true;
1225      }
1226
1227      public final boolean removeAll(final Collection<?> c) {
1228        boolean changed = false;
1229        final Set<R> changes = new HashSet<R>();
1230        for (R row : this)
1231          if (c.contains(row))
1232            changes.add(row);
1233        for (Object o1 : c) {
1234          final int i = rowHeads.indexOf(o1);
1235          if (matrix.getBoolean(i, j)) {
1236            matrix.setBoolean(false, i, j);
1237            changed = true;
1238            push(
1239                new RelationEvent<R, C>(
1240                    RelationEvent.ENTRIES_REMOVED,
1241                    null,
1242                    null,
1243                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<R, Pair<R, C>>() {
1244
1245                      @SuppressWarnings("unchecked")
1246                      public final Pair<R, C> apply(final R row) {
1247                        return new Pair<R, C>(row, (C) o);
1248                      }
1249                    }))));
1250          }
1251        }
1252        return changed;
1253      }
1254
1255      public final boolean retainAll(final Collection<?> c) {
1256        boolean changed = false;
1257        final Set<R> changes = new HashSet<R>();
1258        for (R row : this)
1259          if (!c.contains(row))
1260            changes.add(row);
1261        for (Object o1 : Collections2.filter(rowHeads, Predicates.not(Predicates.in(c)))) {
1262          final int i = rowHeads.indexOf(o1);
1263          if (matrix.getBoolean(i, j)) {
1264            matrix.setBoolean(false, i, j);
1265            changed = true;
1266            push(
1267                new RelationEvent<R, C>(
1268                    RelationEvent.ENTRIES_REMOVED,
1269                    null,
1270                    null,
1271                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<R, Pair<R, C>>() {
1272
1273                      @SuppressWarnings("unchecked")
1274                      public final Pair<R, C> apply(final R row) {
1275                        return new Pair<R, C>(row, (C) o);
1276                      }
1277                    }))));
1278          }
1279        }
1280        return changed;
1281      }
1282
1283      public final void clear() {
1284        for (int i = 0; i < rowHeads.size(); i++)
1285          matrix.setBoolean(false, i, j);
1286      }
1287
1288      public final Iterator<R> iterator() {
1289        return Iterators
1290            .transform(Iterators.filter(ListIterators.integers(0, rowHeads.size()), new Predicate<Integer>() {
1291
1292              public final boolean apply(final Integer i) {
1293                return matrix.getBoolean(i, j);
1294              }
1295            }), new Function<Integer, R>() {
1296
1297              public final R apply(final Integer i) {
1298                return rowHeads.get(i);
1299              }
1300            });
1301      }
1302
1303      public final int size() {
1304        int size = 0;
1305        if (j != -1)
1306          for (int i = 0; i < rowHeads.size(); i++)
1307            if (matrix.getBoolean(i, j))
1308              size++;
1309        return size;
1310      }
1311
1312      public final HashSet<R> clone() {
1313        return new HashSet<R>(this);
1314      }
1315    };
1316  }
1317
1318  public final Set<C> rowAnd(final Collection<?> c) {
1319    if (rowHeads().size() == 0 || colHeads().size() == 0)
1320      return new HashSet<C>(colHeads());
1321    return colHeads().parallelStream().filter(col -> c.parallelStream().allMatch(row -> contains(row, col))).collect(
1322        Collectors.toSet());
1323//    return Sets.filter(colHeads(), new Predicate<C>() {
1324//
1325//      private final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, rowHeads.indicesOf(c, true));
1326//
1327//      public final boolean apply(final C col) {
1328//        return rowAnd.getBoolean(0, colHeads.indexOf(col));
1329//      }
1330//    });
1331  }
1332
1333  public final Set<R> colAnd(final Collection<?> c) {
1334    if (rowHeads().size() == 0 || colHeads().size() == 0)
1335      return new HashSet<R>(rowHeads());
1336    return rowHeads().parallelStream().filter(row -> c.parallelStream().allMatch(col -> contains(row, col))).collect(
1337        Collectors.toSet());
1338//    return Sets.filter(rowHeads(), new Predicate<R>() {
1339//
1340//      private final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, colHeads.indicesOf(c, true));
1341//
1342//      public final boolean apply(final R row) {
1343//        return colAnd.getBoolean(rowHeads.indexOf(row), 0);
1344//      }
1345//    });
1346  }
1347
1348  public final void _add(final int i, final int j) {
1349    matrix.setBoolean(true, i, j);
1350    push(
1351        new RelationEvent<R, C>(
1352            RelationEvent.ENTRIES_ADDED,
1353            null,
1354            null,
1355            Collections.singleton(new Pair<R, C>(rowHeads().get(i), colHeads().get(j)))));
1356  }
1357
1358  public final void _remove(final int i, final int j) {
1359    matrix.setBoolean(false, i, j);
1360  }
1361
1362  public final void _flip(final int i, final int j) {
1363    matrix.setBoolean(matrix.getBoolean(i, j), i, j);
1364  }
1365
1366  public final boolean _contains(final int i, final int j) {
1367    return matrix.getBoolean(i, j);
1368  }
1369
1370  public final Collection<Integer> _row(final int i) {
1371    return _row(i, SetLists.integers(colHeads.size()));
1372  }
1373
1374  public final Collection<Integer> _col(final int j) {
1375    return _col(j, SetLists.integers(rowHeads.size()));
1376  }
1377
1378  public final Collection<Integer> _row(final int i, final Collection<Integer> js) {
1379    final BitSetFX _row = new BitSetFX();
1380    for (int j : js)
1381      if (matrix.getBoolean(i, j))
1382        _row.set(j);
1383    return _row;
1384//    return Collections3.newBitSetFX(Collections2.filter(j, new Predicate<Integer>() {
1385//
1386//      public final boolean apply(final Integer j) {
1387//        return matrix.getBoolean(i, j);
1388//      }
1389//    }));
1390  }
1391
1392  public final Collection<Integer> _col(final int j, final Collection<Integer> is) {
1393    final BitSetFX _col = new BitSetFX();
1394    for (int i : is)
1395      if (matrix.getBoolean(i, j))
1396        _col.set(i);
1397    return _col;
1398//    return Collections3.newBitSetFX(Collections2.filter(i, new Predicate<Integer>() {
1399//
1400//      public final boolean apply(final Integer i) {
1401//        return matrix.getBoolean(i, j);
1402//      }
1403//    }));
1404  }
1405
1406  public final Collection<Integer> _rowAnd(final int... i) {
1407    if (i.length == 1)
1408      return _row(i[0]);
1409    return _rowAnd(Ints.asList(i));
1410  }
1411
1412  public final Collection<Integer> _colAnd(final int... j) {
1413    if (j.length == 1)
1414      return _col(j[0]);
1415    return _colAnd(Ints.asList(j));
1416  }
1417
1418  public synchronized final BitSetFX _rowAnd(final Iterable<Integer> i) {
1419//    return _rowAnd(i, SetLists.integers(colHeads.size()));
1420    if (rowHeads().size() == 0 || colHeads().size() == 0)
1421      return Collections3.integers(colHeads.size());
1422//      return SetLists.integers(colHeads.size());
1423    final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, i);
1424    BitSetFX _rowAnd = new BitSetFX();
1425    for (int j = 0; j < colHeads.size(); j++)
1426      if (rowAnd.getBoolean(0, j))
1427        _rowAnd.add(j);
1428    return _rowAnd;
1429  }
1430
1431  public synchronized final BitSetFX _colAnd(final Iterable<Integer> j) {
1432//    return _colAnd(j, SetLists.integers(rowHeads.size()));
1433    if (rowHeads().size() == 0 || colHeads().size() == 0)
1434      return Collections3.integers(rowHeads().size());
1435//      return SetLists.integers(rowHeads.size());
1436    final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, j);
1437    BitSetFX _colAnd = new BitSetFX();
1438    for (int i = 0; i < rowHeads.size(); i++)
1439      if (colAnd.getBoolean(i, 0))
1440        _colAnd.add(i);
1441    return _colAnd;
1442  }
1443
1444  public final BitSetFX _rowAnd(final Iterable<Integer> i, final Collection<Integer> j) {
1445    if (rowHeads().size() == 0 || colHeads().size() == 0)
1446      return Collections3.integers(colHeads.size());
1447//      return SetLists.integers(colHeads.size());
1448    final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, i);
1449    return j.parallelStream().filter(_j -> rowAnd.getBoolean(0, _j)).collect(
1450        BitSetFX::new,
1451        BitSetFX::add,
1452        BitSetFX::addAll);
1453//    return Collections3.newBitSetFX(Collections2.filter(j, new Predicate<Integer>() {
1454//
1455//      private final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, i);
1456//
1457//      public final boolean apply(final Integer _j) {
1458//        return rowAnd.getBoolean(0, _j);
1459//      }
1460//    }));
1461  }
1462
1463  public final BitSetFX _colAnd(final Iterable<Integer> j, final Collection<Integer> i) {
1464    if (rowHeads().size() == 0 || colHeads().size() == 0)
1465      return Collections3.integers(rowHeads().size());
1466//      return SetLists.integers(rowHeads.size());
1467    final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, j);
1468    return i.parallelStream().filter(_i -> colAnd.getBoolean(_i, 0)).collect(
1469        BitSetFX::new,
1470        BitSetFX::add,
1471        BitSetFX::addAll);
1472//    return Collections3.newBitSetFX(Collections2.filter(i, new Predicate<Integer>() {
1473//
1474//      private final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, j);
1475//
1476//      public final boolean apply(final Integer _i) {
1477//        return colAnd.getBoolean(_i, 0);
1478//      }
1479//    }));
1480  }
1481
1482  public final void empty() {
1483    matrix = BooleanMatrices.empty(Math.max(rowHeads.size(), 1), Math.max(colHeads.size(), 1));
1484    push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED));
1485  }
1486
1487  public final void fill() {
1488    matrix = BooleanMatrices.full(Math.max(rowHeads.size(), 1), Math.max(colHeads.size(), 1));
1489    push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED));
1490  }
1491
1492  public final boolean isEmpty() {
1493    return !matrix.containsBoolean(true);
1494  }
1495
1496  public final boolean isFull() {
1497    return !matrix.containsBoolean(false);
1498  }
1499
1500  public final int size() {
1501    int size = 0;
1502    for (int i = 0; i < rowHeads.size(); i++)
1503      for (int j = 0; j < colHeads.size(); j++)
1504        if (matrix.getBoolean(i, j))
1505          size++;
1506    return size;
1507  }
1508
1509  public final Iterator<Pair<R, C>> iterator() {
1510    return Iterators.transform(Iterators.filter(matrix.allCoordinates().iterator(), new Predicate<long[]>() {
1511
1512      public final boolean apply(final long[] ij) {
1513        return matrix.getBoolean(ij) && ij[0] < rowHeads.size() && ij[1] < colHeads.size();
1514      }
1515    }), new Function<long[], Pair<R, C>>() {
1516
1517      public final Pair<R, C> apply(final long[] ij) {
1518        return new Pair<R, C>(rowHeads.get((int) ij[0]), colHeads.get((int) ij[1]));
1519      }
1520    });
1521  }
1522
1523  public Relation<R, C> subRelation(final Collection<?> rows, final Collection<?> cols) {
1524    return new AbstractRelation<R, C>(
1525        SetLists.intersection(rowHeads, rows),
1526        SetLists.intersection(colHeads, cols),
1527        false) {
1528
1529      public final boolean contains(final Object o1, final Object o2) {
1530        return rowHeads().contains(o1) && colHeads().contains(o2) && MatrixRelation.this.contains(o1, o2);
1531      }
1532
1533      public final MatrixRelation<R, C> clone() {
1534        return new MatrixRelation<R, C>(
1535            rowHeads(),
1536            colHeads(),
1537            (BooleanMatrix) MatrixRelation.this.matrix
1538                .selectRows(Ret.NEW, MatrixRelation.this.rowHeads().indicesOf(rows, false))
1539                .selectColumns(Ret.NEW, MatrixRelation.this.colHeads().indicesOf(cols, false)),
1540            false);
1541      }
1542    };
1543  }
1544
1545  public final void pushAllChangedEvent() {
1546    synchronized (eventHandlers) {
1547      push(RelationEvent.ALL_CHANGED, new RelationEvent<R, C>(RelationEvent.ALL_CHANGED));
1548    }
1549  }
1550
1551  protected final void push(final RelationEvent<R, C> event) {
1552    synchronized (eventHandlers) {
1553      RelationEvent.Type type = event.getType();
1554      push(type, event);
1555      while ((type = type.getSuperType()) != null)
1556        push(type, event);
1557    }
1558  }
1559
1560  private final void push(final RelationEvent.Type type, final RelationEvent<R, C> event) {
1561    synchronized (eventHandlers) {
1562      if (eventHandlers.containsKey(type))
1563        for (RelationEventHandler<R, C> eventHandler : eventHandlers.get(type))
1564          eventHandler.handle(event);
1565    }
1566  }
1567
1568  public final void addEventHandler(final RelationEventHandler<R, C> eventHandler, final RelationEvent.Type... types) {
1569    synchronized (eventHandlers) {
1570      for (RelationEvent.Type type : types) {
1571        if (!eventHandlers.containsKey(type))
1572          eventHandlers.put(type, new CopyOnWriteArrayList<>());
1573        eventHandlers.get(type).add(eventHandler);
1574      }
1575    }
1576  }
1577
1578  public final void removeEventHandler(final RelationEvent.Type type, final RelationEventHandler<R, C> eventHandler) {
1579    synchronized (eventHandlers) {
1580      eventHandlers.remove(type, eventHandler);
1581    }
1582  }
1583
1584  protected final boolean hasEventHandlers(final RelationEvent.Type type) {
1585    synchronized (eventHandlers) {
1586      RelationEvent.Type type_ = type;
1587      if (!eventHandlers.get(type_).isEmpty())
1588        return true;
1589      while ((type_ = type_.getSuperType()) != null)
1590        if (!eventHandlers.get(type_).isEmpty())
1591          return true;
1592      return false;
1593    }
1594  }
1595
1596  public final BooleanMatrix matrix() {
1597    return matrix;
1598  }
1599
1600  public final void setMatrix(final BooleanMatrix matrix) {
1601    if (matrix.getSize(0) != rowHeads.size() || matrix.getSize(1) != colHeads.size())
1602      throw new IllegalArgumentException();
1603    this.matrix = matrix;
1604    pushAllChangedEvent();
1605  }
1606
1607  public final void setContent(final SetList<R> rows, final SetList<C> cols, final BooleanMatrix matrix) {
1608    rowHeads.addAll(rows);
1609    if (!homogen)
1610      colHeads.addAll(cols);
1611    setMatrix(matrix);
1612  }
1613
1614  public void dispose() {
1615    rowHeads.clear();
1616    colHeads.clear();
1617  }
1618
1619  public MatrixRelation<R, C> clone() {
1620    return new MatrixRelation<R, C>(rowHeads, colHeads, BooleanMatrices.clone(matrix), homogen);
1621  }
1622
1623  public int hashCode() {
1624    return 7 * rowHeads.hashCode() + 11 * colHeads.hashCode() + 13 * matrix.hashCode();
1625  }
1626
1627  public final boolean[][] toArray() {
1628    return matrix.toBooleanArray();
1629  }
1630
1631  public Relation<R, R> subRelation(final Collection<?> c) {
1632    return new AbstractRelation<R, R>(
1633        SetLists.<R> intersection(rowHeads, c),
1634        SetLists.<R> intersection(rowHeads, c),
1635        true) {
1636
1637      public boolean contains(Object o1, Object o2) {
1638        return rowHeads().contains(o1) && rowHeads().contains(o2) && MatrixRelation.this.contains(o1, o2);
1639      }
1640
1641      public MatrixRelation<R, R> clone() {
1642        return new MatrixRelation<R, R>(
1643            rowHeads(),
1644            rowHeads(),
1645            (BooleanMatrix) MatrixRelation.this.matrix
1646                .selectRows(Ret.NEW, MatrixRelation.this.rowHeads().indicesOf(c, false))
1647                .selectColumns(Ret.NEW, MatrixRelation.this.colHeads().indicesOf(c, false)),
1648            true);
1649      }
1650    };
1651  }
1652
1653  public MatrixRelation<R, R> neighborhood() {
1654    checkHomogen();
1655    return new MatrixRelation<R, R>(
1656        rowHeads(),
1657        rowHeads(),
1658        BooleanMatrices.transitiveReduction(BooleanMatrices.reflexiveReduction(matrix)),
1659        true);
1660  }
1661
1662  public MatrixRelation<R, R> order() {
1663    checkHomogen();
1664    return new MatrixRelation<R, R>(
1665        rowHeads(),
1666        rowHeads(),
1667        BooleanMatrices.reflexiveClosure(BooleanMatrices.transitiveClosure(matrix)),
1668        true);
1669  }
1670
1671  public final boolean isReflexive() {
1672    return matrix.ge(Ret.NEW, BooleanMatrices.identity(size())).equals(BooleanMatrices.full(size()));
1673  }
1674
1675  public final boolean isIrreflexive() {
1676    return matrix.and(Ret.NEW, BooleanMatrices.identity(size())).equals(BooleanMatrices.empty(size()));
1677  }
1678
1679  public final boolean isSymmetric() {
1680    return matrix.equals(matrix.transpose(Ret.NEW));
1681  }
1682
1683  public final boolean isAsymmetric() {
1684    return matrix.and(Ret.NEW, matrix.transpose(Ret.NEW)).equals(BooleanMatrices.empty(size()));
1685  }
1686
1687  public final boolean isConnex() {
1688    return matrix.or(Ret.NEW, matrix.transpose(Ret.NEW)).equals(BooleanMatrices.full(size()));
1689  }
1690
1691  public final boolean isAntisymmetric() {
1692    return matrix.and(Ret.NEW, matrix.transpose(Ret.NEW)).le(Ret.NEW, BooleanMatrices.identity(size())).equals(
1693        BooleanMatrices.full(size()));
1694  }
1695
1696  public final boolean isQuasiconnex() {
1697    return matrix.or(Ret.NEW, matrix.transpose(Ret.NEW)).ge(Ret.NEW, BooleanMatrices.negativeIdentity(size())).equals(
1698        BooleanMatrices.full(size()));
1699  }
1700
1701  public final boolean isAlternative() {
1702    return isAntisymmetric() && isQuasiconnex();
1703  }
1704
1705  public final boolean isTransitive() {
1706    return matrix
1707        .mtimes(Ret.NEW, false, matrix)
1708        .toBooleanMatrix()
1709        .le(Ret.NEW, matrix)
1710        .equals(BooleanMatrices.full(size()));
1711  }
1712
1713  public final boolean isNegativeTransitive() {
1714    final BooleanMatrix2D not = (BooleanMatrix2D) matrix.not(Ret.NEW);
1715    return not.mtimes(Ret.NEW, false, not).toBooleanMatrix().le(Ret.NEW, not).equals(BooleanMatrices.full(size()));
1716  }
1717
1718  public final boolean isAtransitive() {
1719    return matrix
1720        .mtimes(Ret.NEW, false, matrix)
1721        .toBooleanMatrix()
1722        .and(Ret.NEW, matrix)
1723        .equals(BooleanMatrices.empty(size()));
1724  }
1725
1726  public final boolean isNegativAtransitive() {
1727    final BooleanMatrix2D not = (BooleanMatrix2D) matrix.not(Ret.NEW);
1728    return not.mtimes(Ret.NEW, false, not).toBooleanMatrix().and(Ret.NEW, not).equals(BooleanMatrices.empty(size()));
1729  }
1730
1731  public final boolean isNCyclic(final int n) {
1732    return BooleanMatrices
1733        .power(matrix(), n)
1734        .le(Ret.NEW, matrix.transpose(Ret.NEW))
1735        .equals(BooleanMatrices.full(size()));
1736  }
1737
1738  public final boolean isCyclic() {
1739    return BooleanMatrices
1740        .transitiveClosure(matrix())
1741        .le(Ret.NEW, matrix.transpose(Ret.NEW))
1742        .equals(BooleanMatrices.full(size()));
1743  }
1744
1745  public final boolean isNAcyclic(final int n) {
1746    return BooleanMatrices
1747        .power(matrix(), n)
1748        .le(Ret.NEW, matrix.transpose(Ret.NEW).not(Ret.NEW))
1749        .equals(BooleanMatrices.full(size()));
1750  }
1751
1752  public final boolean isAcyclic() {
1753    return BooleanMatrices.transitiveClosure(matrix()).le(Ret.NEW, matrix.transpose(Ret.NEW).not(Ret.NEW)).equals(
1754        BooleanMatrices.full(size()));
1755  }
1756
1757  public final boolean isNTransitive(final int n) {
1758    return BooleanMatrices.power(matrix(), n).le(Ret.NEW, matrix).equals(BooleanMatrices.full(size()));
1759  }
1760
1761  public final boolean isNAtransitive(final int n) {
1762    return BooleanMatrices.power(matrix(), n).le(Ret.NEW, matrix.not(Ret.NEW)).equals(BooleanMatrices.full(size()));
1763  }
1764
1765  public final boolean isLeftComparative() {
1766    return matrix.transpose(Ret.NEW).mtimes(Ret.NEW, false, matrix).toBooleanMatrix().le(Ret.NEW, matrix).equals(
1767        BooleanMatrices.full(size()));
1768  }
1769
1770  public final boolean isRightComparative() {
1771    return matrix.mtimes(Ret.NEW, false, matrix.transpose(Ret.NEW)).toBooleanMatrix().le(Ret.NEW, matrix).equals(
1772        BooleanMatrices.full(size()));
1773  }
1774}