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 - 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.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
881                  .selectRows(Ret.LINK, _r.rowHeads.indicesOf(rowHeads, false))
882                  .selectColumns(Ret.LINK, _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
956          .selectRows(Ret.LINK, i)
957          .selectColumns(Ret.LINK, j)
958          .and(
959              Ret.ORIG,
960              _r.matrix
961                  .selectRows(Ret.LINK, _r.rowHeads.indicesOf(rowHeads, false))
962                  .selectColumns(Ret.LINK, _r.colHeads.indicesOf(colHeads, false)));
963      push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED, null, null, null));
964      changed = true;
965    } else {
966      for (C col : colHeads) {
967        final Set<R> column = col(col);
968        if (r.colHeads().contains(col))
969          changed |= column.retainAll(r.col(col));
970        else {
971          changed |= !column.isEmpty();
972          column.clear();
973        }
974      }
975    }
976    return changed;
977  }
978
979  public final boolean contains(final Object o1, final Object o2) {
980    final int i = rowHeads.indexOf(o1);
981    if (i == -1)
982      return false;
983    final int j = colHeads.indexOf(o2);
984    return j != -1 && matrix.getBoolean(i, j);
985  }
986
987  public final boolean containsAll(final Relation<?, ?> r) {
988    if (rowHeads.containsAll(r.rowHeads()) && colHeads.containsAll(r.colHeads())) {
989      if (r instanceof MatrixRelation) {
990        final MatrixRelation<?, ?> _r = (MatrixRelation<?, ?>) r;
991        return matrix
992            .selectRows(Ret.LINK, rowHeads.indicesOf(_r.rowHeads, true))
993            .selectColumns(Ret.LINK, colHeads.indicesOf(_r.colHeads, true))
994            .equals(_r.matrix);
995      } else {
996        final Iterator<?> iterator = r.iterator();
997        Pair<?, ?> next;
998        while (iterator.hasNext()) {
999          next = (Pair<?, ?>) iterator.next();
1000          if (!contains(next.x(), next.y()))
1001            return false;
1002        }
1003        return true;
1004      }
1005    }
1006    return false;
1007  }
1008
1009  public final Set<C> row(final Object o) {
1010    return new AbstractSet<C>() {
1011
1012      private final int i = rowHeads.indexOf(o);
1013
1014      @SuppressWarnings("unchecked")
1015      public final boolean add(final C col) {
1016        final int j = colHeads.indexOf(col);
1017        if (matrix.getBoolean(i, j))
1018          return false;
1019        matrix.setBoolean(true, i, j);
1020        push(
1021            new RelationEvent<R, C>(
1022                RelationEvent.ENTRIES_ADDED,
1023                null,
1024                null,
1025                Collections.singleton(new Pair<R, C>((R) o, col))));
1026        return true;
1027      }
1028
1029      public final boolean addAll(final Collection<? extends C> c) {
1030        boolean changed = false;
1031        @SuppressWarnings("unchecked")
1032        final Set<C> changes = new HashSet<C>(Collections3.<C> difference((Collection<C>) c, this));
1033        for (C col : c) {
1034          final int j = colHeads.indexOf(col);
1035          if (!matrix.getBoolean(i, j)) {
1036            matrix.setBoolean(true, i, j);
1037            changed = true;
1038            push(
1039                new RelationEvent<R, C>(
1040                    RelationEvent.ENTRIES_ADDED,
1041                    null,
1042                    null,
1043                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<C, Pair<R, C>>() {
1044
1045                      @SuppressWarnings("unchecked")
1046                      public final Pair<R, C> apply(final C col) {
1047                        return new Pair<R, C>((R) o, col);
1048                      }
1049                    }))));
1050          }
1051        }
1052        return changed;
1053      }
1054
1055      public final boolean contains(final Object o) {
1056        return matrix.getBoolean(i, colHeads.indexOf(o));
1057      }
1058
1059      @SuppressWarnings("unchecked")
1060      public final boolean remove(final Object o2) {
1061        final int j = colHeads.indexOf(o2);
1062        if (!matrix.getBoolean(i, j))
1063          return false;
1064        matrix.setBoolean(false, i, j);
1065        push(
1066            new RelationEvent<R, C>(
1067                RelationEvent.ENTRIES_REMOVED,
1068                null,
1069                null,
1070                Collections.singleton(new Pair<R, C>((R) o, (C) o2))));
1071        return true;
1072      }
1073
1074      public final boolean removeAll(final Collection<?> c) {
1075        boolean changed = false;
1076        final Set<C> changes = new HashSet<C>();
1077        for (C col : this)
1078          if (c.contains(col))
1079            changes.add(col);
1080        for (Object o2 : c) {
1081          final int j = colHeads.indexOf(o2);
1082          if (matrix.getBoolean(i, j)) {
1083            matrix.setBoolean(false, i, j);
1084            changed = true;
1085            push(
1086                new RelationEvent<R, C>(
1087                    RelationEvent.ENTRIES_REMOVED,
1088                    null,
1089                    null,
1090                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<C, Pair<R, C>>() {
1091
1092                      @SuppressWarnings("unchecked")
1093                      public final Pair<R, C> apply(final C col) {
1094                        return new Pair<R, C>((R) o, col);
1095                      }
1096                    }))));
1097          }
1098        }
1099        return changed;
1100      }
1101
1102      public final boolean retainAll(final Collection<?> c) {
1103        boolean changed = false;
1104        final Set<C> changes = new HashSet<C>();
1105        for (C col : this)
1106          if (!c.contains(col))
1107            changes.add(col);
1108        for (Object o2 : Collections2.filter(colHeads, Predicates.not(Predicates.in(c)))) {
1109          final int j = colHeads.indexOf(o2);
1110          if (matrix.getBoolean(i, j)) {
1111            matrix.setBoolean(false, i, j);
1112            changed = true;
1113            push(
1114                new RelationEvent<R, C>(
1115                    RelationEvent.ENTRIES_REMOVED,
1116                    null,
1117                    null,
1118                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<C, Pair<R, C>>() {
1119
1120                      @SuppressWarnings("unchecked")
1121                      public final Pair<R, C> apply(final C col) {
1122                        return new Pair<R, C>((R) o, col);
1123                      }
1124                    }))));
1125          }
1126        }
1127        return changed;
1128      }
1129
1130      public final void clear() {
1131        for (int j = 0; j < colHeads.size(); j++)
1132          matrix.setBoolean(false, i, j);
1133      }
1134
1135      public final Iterator<C> iterator() {
1136        return Iterators
1137            .transform(Iterators.filter(ListIterators.integers(0, colHeads.size()), new Predicate<Integer>() {
1138
1139              public final boolean apply(final Integer j) {
1140                return matrix.getBoolean(i, j);
1141              }
1142            }), new Function<Integer, C>() {
1143
1144              public final C apply(final Integer j) {
1145                return colHeads.get(j);
1146              }
1147            });
1148      }
1149
1150      public final int size() {
1151        int size = 0;
1152        if (i != -1)
1153          for (int j = 0; j < colHeads.size(); j++)
1154            if (matrix.getBoolean(i, j))
1155              size++;
1156        return size;
1157      }
1158
1159      public final HashSet<C> clone() {
1160        return new HashSet<C>(this);
1161      }
1162    };
1163  }
1164
1165  public final Set<R> col(final Object o) {
1166    return new AbstractSet<R>() {
1167
1168      private final int j = colHeads.indexOf(o);
1169
1170      @SuppressWarnings("unchecked")
1171      public final boolean add(final R row) {
1172        final int i = rowHeads.indexOf(row);
1173        if (matrix.getBoolean(i, j))
1174          return false;
1175        matrix.setBoolean(true, i, j);
1176        push(
1177            new RelationEvent<R, C>(
1178                RelationEvent.ENTRIES_ADDED,
1179                null,
1180                null,
1181                Collections.singleton(new Pair<R, C>(row, (C) o))));
1182        return true;
1183      };
1184
1185      public final boolean addAll(Collection<? extends R> c) {
1186        boolean changed = false;
1187        @SuppressWarnings("unchecked")
1188        final Set<R> changes = new HashSet<R>(Collections3.<R> difference((Collection<R>) c, this));
1189        for (R row : c) {
1190          final int i = rowHeads.indexOf(row);
1191          if (!matrix.getBoolean(i, j)) {
1192            matrix.setBoolean(true, i, j);
1193            changed = true;
1194            push(
1195                new RelationEvent<R, C>(
1196                    RelationEvent.ENTRIES_ADDED,
1197                    null,
1198                    null,
1199                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<R, Pair<R, C>>() {
1200
1201                      @SuppressWarnings("unchecked")
1202                      public final Pair<R, C> apply(final R row) {
1203                        return new Pair<R, C>(row, (C) o);
1204                      }
1205                    }))));
1206          }
1207        }
1208        return changed;
1209      }
1210
1211      public final boolean contains(final Object o1) {
1212        return matrix.getBoolean(rowHeads.indexOf(o1), j);
1213      }
1214
1215      @SuppressWarnings("unchecked")
1216      public final boolean remove(final Object o1) {
1217        final int i = rowHeads.indexOf(o1);
1218        if (!matrix.getBoolean(i, j))
1219          return false;
1220        matrix.setBoolean(false, i, j);
1221        push(
1222            new RelationEvent<R, C>(
1223                RelationEvent.ENTRIES_REMOVED,
1224                null,
1225                null,
1226                Collections.singleton(new Pair<R, C>((R) o1, (C) o))));
1227        return true;
1228      }
1229
1230      public final boolean removeAll(final Collection<?> c) {
1231        boolean changed = false;
1232        final Set<R> changes = new HashSet<R>();
1233        for (R row : this)
1234          if (c.contains(row))
1235            changes.add(row);
1236        for (Object o1 : c) {
1237          final int i = rowHeads.indexOf(o1);
1238          if (matrix.getBoolean(i, j)) {
1239            matrix.setBoolean(false, i, j);
1240            changed = true;
1241            push(
1242                new RelationEvent<R, C>(
1243                    RelationEvent.ENTRIES_REMOVED,
1244                    null,
1245                    null,
1246                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<R, Pair<R, C>>() {
1247
1248                      @SuppressWarnings("unchecked")
1249                      public final Pair<R, C> apply(final R row) {
1250                        return new Pair<R, C>(row, (C) o);
1251                      }
1252                    }))));
1253          }
1254        }
1255        return changed;
1256      }
1257
1258      public final boolean retainAll(final Collection<?> c) {
1259        boolean changed = false;
1260        final Set<R> changes = new HashSet<R>();
1261        for (R row : this)
1262          if (!c.contains(row))
1263            changes.add(row);
1264        for (Object o1 : Collections2.filter(rowHeads, Predicates.not(Predicates.in(c)))) {
1265          final int i = rowHeads.indexOf(o1);
1266          if (matrix.getBoolean(i, j)) {
1267            matrix.setBoolean(false, i, j);
1268            changed = true;
1269            push(
1270                new RelationEvent<R, C>(
1271                    RelationEvent.ENTRIES_REMOVED,
1272                    null,
1273                    null,
1274                    new HashSet<Pair<R, C>>(Collections2.transform(changes, new Function<R, Pair<R, C>>() {
1275
1276                      @SuppressWarnings("unchecked")
1277                      public final Pair<R, C> apply(final R row) {
1278                        return new Pair<R, C>(row, (C) o);
1279                      }
1280                    }))));
1281          }
1282        }
1283        return changed;
1284      }
1285
1286      public final void clear() {
1287        for (int i = 0; i < rowHeads.size(); i++)
1288          matrix.setBoolean(false, i, j);
1289      }
1290
1291      public final Iterator<R> iterator() {
1292        return Iterators
1293            .transform(Iterators.filter(ListIterators.integers(0, rowHeads.size()), new Predicate<Integer>() {
1294
1295              public final boolean apply(final Integer i) {
1296                return matrix.getBoolean(i, j);
1297              }
1298            }), new Function<Integer, R>() {
1299
1300              public final R apply(final Integer i) {
1301                return rowHeads.get(i);
1302              }
1303            });
1304      }
1305
1306      public final int size() {
1307        int size = 0;
1308        if (j != -1)
1309          for (int i = 0; i < rowHeads.size(); i++)
1310            if (matrix.getBoolean(i, j))
1311              size++;
1312        return size;
1313      }
1314
1315      public final HashSet<R> clone() {
1316        return new HashSet<R>(this);
1317      }
1318    };
1319  }
1320
1321  public final Set<C> rowAnd(final Collection<?> c) {
1322    if (rowHeads().size() == 0 || colHeads().size() == 0)
1323      return new HashSet<C>(colHeads());
1324    return colHeads()
1325        .parallelStream()
1326        .filter(col -> c.parallelStream().allMatch(row -> contains(row, col)))
1327        .collect(Collectors.toSet());
1328//    return Sets.filter(colHeads(), new Predicate<C>() {
1329//
1330//      private final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, rowHeads.indicesOf(c, true));
1331//
1332//      public final boolean apply(final C col) {
1333//        return rowAnd.getBoolean(0, colHeads.indexOf(col));
1334//      }
1335//    });
1336  }
1337
1338  public final Set<R> colAnd(final Collection<?> c) {
1339    if (rowHeads().size() == 0 || colHeads().size() == 0)
1340      return new HashSet<R>(rowHeads());
1341    return rowHeads()
1342        .parallelStream()
1343        .filter(row -> c.parallelStream().allMatch(col -> contains(row, col)))
1344        .collect(Collectors.toSet());
1345//    return Sets.filter(rowHeads(), new Predicate<R>() {
1346//
1347//      private final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, colHeads.indicesOf(c, true));
1348//
1349//      public final boolean apply(final R row) {
1350//        return colAnd.getBoolean(rowHeads.indexOf(row), 0);
1351//      }
1352//    });
1353  }
1354
1355  public final void _add(final int i, final int j) {
1356    matrix.setBoolean(true, i, j);
1357    push(
1358        new RelationEvent<R, C>(
1359            RelationEvent.ENTRIES_ADDED,
1360            null,
1361            null,
1362            Collections.singleton(new Pair<R, C>(rowHeads().get(i), colHeads().get(j)))));
1363  }
1364
1365  public final void _remove(final int i, final int j) {
1366    matrix.setBoolean(false, i, j);
1367  }
1368
1369  public final void _flip(final int i, final int j) {
1370    matrix.setBoolean(matrix.getBoolean(i, j), i, j);
1371  }
1372
1373  public final boolean _contains(final int i, final int j) {
1374    return matrix.getBoolean(i, j);
1375  }
1376
1377  public final Collection<Integer> _row(final int i) {
1378    return _row(i, SetLists.integers(colHeads.size()));
1379  }
1380
1381  public final Collection<Integer> _col(final int j) {
1382    return _col(j, SetLists.integers(rowHeads.size()));
1383  }
1384
1385  public final Collection<Integer> _row(final int i, final Collection<Integer> js) {
1386    final BitSetFX _row = new BitSetFX();
1387    for (int j : js)
1388      if (matrix.getBoolean(i, j))
1389        _row.set(j);
1390    return _row;
1391//    return Collections3.newBitSetFX(Collections2.filter(j, new Predicate<Integer>() {
1392//
1393//      public final boolean apply(final Integer j) {
1394//        return matrix.getBoolean(i, j);
1395//      }
1396//    }));
1397  }
1398
1399  public final Collection<Integer> _col(final int j, final Collection<Integer> is) {
1400    final BitSetFX _col = new BitSetFX();
1401    for (int i : is)
1402      if (matrix.getBoolean(i, j))
1403        _col.set(i);
1404    return _col;
1405//    return Collections3.newBitSetFX(Collections2.filter(i, new Predicate<Integer>() {
1406//
1407//      public final boolean apply(final Integer i) {
1408//        return matrix.getBoolean(i, j);
1409//      }
1410//    }));
1411  }
1412
1413  public final Collection<Integer> _rowAnd(final int... i) {
1414    if (i.length == 1)
1415      return _row(i[0]);
1416    return _rowAnd(Ints.asList(i));
1417  }
1418
1419  public final Collection<Integer> _colAnd(final int... j) {
1420    if (j.length == 1)
1421      return _col(j[0]);
1422    return _colAnd(Ints.asList(j));
1423  }
1424
1425  public synchronized final BitSetFX _rowAnd(final Iterable<Integer> i) {
1426//    return _rowAnd(i, SetLists.integers(colHeads.size()));
1427    if (rowHeads().size() == 0 || colHeads().size() == 0)
1428      return Collections3.integers(colHeads.size());
1429//      return SetLists.integers(colHeads.size());
1430    final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, i);
1431    BitSetFX _rowAnd = new BitSetFX();
1432    for (int j = 0; j < colHeads.size(); j++)
1433      if (rowAnd.getBoolean(0, j))
1434        _rowAnd.add(j);
1435    return _rowAnd;
1436  }
1437
1438  public synchronized final BitSetFX _colAnd(final Iterable<Integer> j) {
1439//    return _colAnd(j, SetLists.integers(rowHeads.size()));
1440    if (rowHeads().size() == 0 || colHeads().size() == 0)
1441      return Collections3.integers(rowHeads().size());
1442//      return SetLists.integers(rowHeads.size());
1443    final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, j);
1444    BitSetFX _colAnd = new BitSetFX();
1445    for (int i = 0; i < rowHeads.size(); i++)
1446      if (colAnd.getBoolean(i, 0))
1447        _colAnd.add(i);
1448    return _colAnd;
1449  }
1450
1451  public final BitSetFX _rowAnd(final Iterable<Integer> i, final Collection<Integer> j) {
1452    if (rowHeads().size() == 0 || colHeads().size() == 0)
1453      return Collections3.integers(colHeads.size());
1454//      return SetLists.integers(colHeads.size());
1455    final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, i);
1456    return j
1457        .parallelStream()
1458        .filter(_j -> rowAnd.getBoolean(0, _j))
1459        .collect(BitSetFX::new, BitSetFX::add, BitSetFX::addAll);
1460//    return Collections3.newBitSetFX(Collections2.filter(j, new Predicate<Integer>() {
1461//
1462//      private final BooleanMatrix rowAnd = BooleanMatrices.andRow(matrix, i);
1463//
1464//      public final boolean apply(final Integer _j) {
1465//        return rowAnd.getBoolean(0, _j);
1466//      }
1467//    }));
1468  }
1469
1470  public final BitSetFX _colAnd(final Iterable<Integer> j, final Collection<Integer> i) {
1471    if (rowHeads().size() == 0 || colHeads().size() == 0)
1472      return Collections3.integers(rowHeads().size());
1473//      return SetLists.integers(rowHeads.size());
1474    final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, j);
1475    return i
1476        .parallelStream()
1477        .filter(_i -> colAnd.getBoolean(_i, 0))
1478        .collect(BitSetFX::new, BitSetFX::add, BitSetFX::addAll);
1479//    return Collections3.newBitSetFX(Collections2.filter(i, new Predicate<Integer>() {
1480//
1481//      private final BooleanMatrix colAnd = BooleanMatrices.andCol(matrix, j);
1482//
1483//      public final boolean apply(final Integer _i) {
1484//        return colAnd.getBoolean(_i, 0);
1485//      }
1486//    }));
1487  }
1488
1489  public final void empty() {
1490    matrix = BooleanMatrices.empty(Math.max(rowHeads.size(), 1), Math.max(colHeads.size(), 1));
1491    push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED));
1492  }
1493
1494  public final void fill() {
1495    matrix = BooleanMatrices.full(Math.max(rowHeads.size(), 1), Math.max(colHeads.size(), 1));
1496    push(new RelationEvent<R, C>(RelationEvent.ALL_CHANGED));
1497  }
1498
1499  public final boolean isEmpty() {
1500    return !matrix.containsBoolean(true);
1501  }
1502
1503  public final boolean isFull() {
1504    return !matrix.containsBoolean(false);
1505  }
1506
1507  public final int size() {
1508    int size = 0;
1509    for (int i = 0; i < rowHeads.size(); i++)
1510      for (int j = 0; j < colHeads.size(); j++)
1511        if (matrix.getBoolean(i, j))
1512          size++;
1513    return size;
1514  }
1515
1516  public final Iterator<Pair<R, C>> iterator() {
1517    return Iterators.transform(Iterators.filter(matrix.allCoordinates().iterator(), new Predicate<long[]>() {
1518
1519      public final boolean apply(final long[] ij) {
1520        return matrix.getBoolean(ij) && ij[0] < rowHeads.size() && ij[1] < colHeads.size();
1521      }
1522    }), new Function<long[], Pair<R, C>>() {
1523
1524      public final Pair<R, C> apply(final long[] ij) {
1525        return new Pair<R, C>(rowHeads.get((int) ij[0]), colHeads.get((int) ij[1]));
1526      }
1527    });
1528  }
1529
1530  public Relation<R, C> subRelation(final Collection<?> rows, final Collection<?> cols) {
1531    return new AbstractRelation<R, C>(
1532        SetLists.intersection(rowHeads, rows),
1533        SetLists.intersection(colHeads, cols),
1534        false) {
1535
1536      public final boolean contains(final Object o1, final Object o2) {
1537        return rowHeads().contains(o1) && colHeads().contains(o2) && MatrixRelation.this.contains(o1, o2);
1538      }
1539
1540      public final MatrixRelation<R, C> clone() {
1541        return new MatrixRelation<R, C>(
1542            rowHeads(),
1543            colHeads(),
1544            (BooleanMatrix) MatrixRelation.this.matrix
1545                .selectRows(Ret.NEW, MatrixRelation.this.rowHeads().indicesOf(rows, false))
1546                .selectColumns(Ret.NEW, MatrixRelation.this.colHeads().indicesOf(cols, false)),
1547            false);
1548      }
1549    };
1550  }
1551
1552  public final void pushAllChangedEvent() {
1553    synchronized (eventHandlers) {
1554      push(RelationEvent.ALL_CHANGED, new RelationEvent<R, C>(RelationEvent.ALL_CHANGED));
1555    }
1556  }
1557
1558  protected final void push(final RelationEvent<R, C> event) {
1559    synchronized (eventHandlers) {
1560      RelationEvent.Type type = event.getType();
1561      push(type, event);
1562      while ((type = type.getSuperType()) != null)
1563        push(type, event);
1564    }
1565  }
1566
1567  private final void push(final RelationEvent.Type type, final RelationEvent<R, C> event) {
1568    synchronized (eventHandlers) {
1569      if (eventHandlers.containsKey(type))
1570        for (RelationEventHandler<R, C> eventHandler : eventHandlers.get(type))
1571          eventHandler.handle(event);
1572    }
1573  }
1574
1575  public final void addEventHandler(final RelationEventHandler<R, C> eventHandler, final RelationEvent.Type... types) {
1576    synchronized (eventHandlers) {
1577      for (RelationEvent.Type type : types) {
1578        if (!eventHandlers.containsKey(type))
1579          eventHandlers.put(type, new CopyOnWriteArrayList<>());
1580        eventHandlers.get(type).add(eventHandler);
1581      }
1582    }
1583  }
1584
1585  public final void removeEventHandler(final RelationEvent.Type type, final RelationEventHandler<R, C> eventHandler) {
1586    synchronized (eventHandlers) {
1587      eventHandlers.remove(type, eventHandler);
1588    }
1589  }
1590
1591  protected final boolean hasEventHandlers(final RelationEvent.Type type) {
1592    synchronized (eventHandlers) {
1593      RelationEvent.Type type_ = type;
1594      if (!eventHandlers.get(type_).isEmpty())
1595        return true;
1596      while ((type_ = type_.getSuperType()) != null)
1597        if (!eventHandlers.get(type_).isEmpty())
1598          return true;
1599      return false;
1600    }
1601  }
1602
1603  public final BooleanMatrix matrix() {
1604    return matrix;
1605  }
1606
1607  public final void setMatrix(final BooleanMatrix matrix) {
1608    if (matrix.getSize(0) != rowHeads.size() || matrix.getSize(1) != colHeads.size())
1609      throw new IllegalArgumentException();
1610    this.matrix = matrix;
1611    pushAllChangedEvent();
1612  }
1613
1614  @Deprecated
1615  public final void rewriteMatrix() {
1616    final BooleanMatrix copy = BooleanMatrices.clone(matrix);
1617    setMatrix(BooleanMatrices.empty(matrix.getRowCount(), matrix.getColumnCount()));
1618    matrix.or(Ret.ORIG, copy);
1619  }
1620
1621  public final void setContent(final SetList<R> rows, final SetList<C> cols, final BooleanMatrix matrix) {
1622    rowHeads.addAll(rows);
1623    if (!homogen)
1624      colHeads.addAll(cols);
1625    setMatrix(matrix);
1626  }
1627
1628  public void dispose() {
1629    rowHeads.clear();
1630    colHeads.clear();
1631  }
1632
1633  public MatrixRelation<R, C> clone() {
1634    return new MatrixRelation<R, C>(rowHeads, colHeads, BooleanMatrices.clone(matrix), homogen);
1635  }
1636
1637  public int hashCode() {
1638    return 7 * rowHeads.hashCode() + 11 * colHeads.hashCode() + 13 * matrix.hashCode();
1639  }
1640
1641  public final boolean[][] toArray() {
1642    return matrix.toBooleanArray();
1643  }
1644
1645  public Relation<R, R> subRelation(final Collection<?> c) {
1646    return new AbstractRelation<R, R>(
1647        SetLists.<R> intersection(rowHeads, c),
1648        SetLists.<R> intersection(rowHeads, c),
1649        true) {
1650
1651      public boolean contains(Object o1, Object o2) {
1652        return rowHeads().contains(o1) && rowHeads().contains(o2) && MatrixRelation.this.contains(o1, o2);
1653      }
1654
1655      public MatrixRelation<R, R> clone() {
1656        return new MatrixRelation<R, R>(
1657            rowHeads(),
1658            rowHeads(),
1659            (BooleanMatrix) MatrixRelation.this.matrix
1660                .selectRows(Ret.NEW, MatrixRelation.this.rowHeads().indicesOf(c, false))
1661                .selectColumns(Ret.NEW, MatrixRelation.this.colHeads().indicesOf(c, false)),
1662            true);
1663      }
1664    };
1665  }
1666
1667  public MatrixRelation<R, R> neighborhood() {
1668    checkHomogen();
1669    return new MatrixRelation<R, R>(
1670        rowHeads(),
1671        rowHeads(),
1672        BooleanMatrices.transitiveReduction(BooleanMatrices.reflexiveReduction(matrix)),
1673        true);
1674  }
1675
1676  public MatrixRelation<R, R> order() {
1677    checkHomogen();
1678    return new MatrixRelation<R, R>(
1679        rowHeads(),
1680        rowHeads(),
1681        BooleanMatrices.reflexiveClosure(BooleanMatrices.transitiveClosure(matrix)),
1682        true);
1683  }
1684
1685  public final boolean isReflexive() {
1686    return matrix.ge(Ret.NEW, BooleanMatrices.identity(size())).equals(BooleanMatrices.full(size()));
1687  }
1688
1689  public final boolean isIrreflexive() {
1690    return matrix.and(Ret.NEW, BooleanMatrices.identity(size())).equals(BooleanMatrices.empty(size()));
1691  }
1692
1693  public final boolean isSymmetric() {
1694    return matrix.equals(matrix.transpose(Ret.NEW));
1695  }
1696
1697  public final boolean isAsymmetric() {
1698    return matrix.and(Ret.NEW, matrix.transpose(Ret.NEW)).equals(BooleanMatrices.empty(size()));
1699  }
1700
1701  public final boolean isConnex() {
1702    return matrix.or(Ret.NEW, matrix.transpose(Ret.NEW)).equals(BooleanMatrices.full(size()));
1703  }
1704
1705  public final boolean isAntisymmetric() {
1706    return matrix
1707        .and(Ret.NEW, matrix.transpose(Ret.NEW))
1708        .le(Ret.NEW, BooleanMatrices.identity(size()))
1709        .equals(BooleanMatrices.full(size()));
1710  }
1711
1712  public final boolean isQuasiconnex() {
1713    return matrix
1714        .or(Ret.NEW, matrix.transpose(Ret.NEW))
1715        .ge(Ret.NEW, BooleanMatrices.negativeIdentity(size()))
1716        .equals(BooleanMatrices.full(size()));
1717  }
1718
1719  public final boolean isAlternative() {
1720    return isAntisymmetric() && isQuasiconnex();
1721  }
1722
1723  public final boolean isTransitive() {
1724    return matrix
1725        .mtimes(Ret.NEW, false, matrix)
1726        .toBooleanMatrix()
1727        .le(Ret.NEW, matrix)
1728        .equals(BooleanMatrices.full(size()));
1729  }
1730
1731  public final boolean isNegativeTransitive() {
1732    final BooleanMatrix2D not = (BooleanMatrix2D) matrix.not(Ret.NEW);
1733    return not.mtimes(Ret.NEW, false, not).toBooleanMatrix().le(Ret.NEW, not).equals(BooleanMatrices.full(size()));
1734  }
1735
1736  public final boolean isAtransitive() {
1737    return matrix
1738        .mtimes(Ret.NEW, false, matrix)
1739        .toBooleanMatrix()
1740        .and(Ret.NEW, matrix)
1741        .equals(BooleanMatrices.empty(size()));
1742  }
1743
1744  public final boolean isNegativAtransitive() {
1745    final BooleanMatrix2D not = (BooleanMatrix2D) matrix.not(Ret.NEW);
1746    return not.mtimes(Ret.NEW, false, not).toBooleanMatrix().and(Ret.NEW, not).equals(BooleanMatrices.empty(size()));
1747  }
1748
1749  public final boolean isNCyclic(final int n) {
1750    return BooleanMatrices
1751        .power(matrix(), n)
1752        .le(Ret.NEW, matrix.transpose(Ret.NEW))
1753        .equals(BooleanMatrices.full(size()));
1754  }
1755
1756  public final boolean isCyclic() {
1757    return BooleanMatrices
1758        .transitiveClosure(matrix())
1759        .le(Ret.NEW, matrix.transpose(Ret.NEW))
1760        .equals(BooleanMatrices.full(size()));
1761  }
1762
1763  public final boolean isNAcyclic(final int n) {
1764    return BooleanMatrices
1765        .power(matrix(), n)
1766        .le(Ret.NEW, matrix.transpose(Ret.NEW).not(Ret.NEW))
1767        .equals(BooleanMatrices.full(size()));
1768  }
1769
1770  public final boolean isAcyclic() {
1771    return BooleanMatrices
1772        .transitiveClosure(matrix())
1773        .le(Ret.NEW, matrix.transpose(Ret.NEW).not(Ret.NEW))
1774        .equals(BooleanMatrices.full(size()));
1775  }
1776
1777  public final boolean isNTransitive(final int n) {
1778    return BooleanMatrices.power(matrix(), n).le(Ret.NEW, matrix).equals(BooleanMatrices.full(size()));
1779  }
1780
1781  public final boolean isNAtransitive(final int n) {
1782    return BooleanMatrices.power(matrix(), n).le(Ret.NEW, matrix.not(Ret.NEW)).equals(BooleanMatrices.full(size()));
1783  }
1784
1785  public final boolean isLeftComparative() {
1786    return matrix
1787        .transpose(Ret.NEW)
1788        .mtimes(Ret.NEW, false, matrix)
1789        .toBooleanMatrix()
1790        .le(Ret.NEW, matrix)
1791        .equals(BooleanMatrices.full(size()));
1792  }
1793
1794  public final boolean isRightComparative() {
1795    return matrix
1796        .mtimes(Ret.NEW, false, matrix.transpose(Ret.NEW))
1797        .toBooleanMatrix()
1798        .le(Ret.NEW, matrix)
1799        .equals(BooleanMatrices.full(size()));
1800  }
1801}