001package conexp.fx.core.collections.relation;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2019 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.HashSet;
028import java.util.Iterator;
029import java.util.ListIterator;
030import java.util.NoSuchElementException;
031import java.util.Set;
032import java.util.function.BiPredicate;
033
034import com.google.common.base.Predicate;
035import com.google.common.collect.Iterators;
036import com.google.common.collect.Sets;
037
038import conexp.fx.core.collections.Collections3;
039import conexp.fx.core.collections.ListIterators;
040import conexp.fx.core.collections.Pair;
041import conexp.fx.core.collections.SimpleListIterator;
042import conexp.fx.core.collections.setlist.AbstractSetList;
043import conexp.fx.core.collections.setlist.SetList;
044import conexp.fx.core.collections.setlist.SetLists;
045
046public abstract class AbstractRelation<R, C> implements Relation<R, C> {
047
048  public static <R, C> AbstractRelation<R, C>
049      fromPredicate(final SetList<R> rowHeads, final SetList<C> colHeads, final BiPredicate<R, C> predicate) {
050    return new AbstractRelation<R, C>(rowHeads, colHeads, false) {
051
052      @Override
053      public final boolean contains(final Object o1, final Object o2) {
054        try {
055          return predicate.test((R) o1, (C) o2);
056        } catch (ClassCastException e) {
057          return false;
058        }
059      }
060
061    };
062  }
063
064  public static <R> AbstractRelation<R, R> fromPredicate(final SetList<R> heads, final BiPredicate<R, R> predicate) {
065    return new AbstractRelation<R, R>(heads, heads, true) {
066
067      @Override
068      public final boolean contains(final Object o1, final Object o2) {
069        try {
070          return predicate.test((R) o1, (R) o2);
071        } catch (ClassCastException e) {
072          return false;
073        }
074      }
075
076    };
077  }
078
079  protected final boolean homogen;
080  protected SetList<R>    rowHeads;
081  protected SetList<C>    colHeads;
082
083  @SuppressWarnings("unchecked")
084  protected AbstractRelation(final SetList<R> rowHeads, final SetList<C> colHeads, final boolean homogen) {
085    this(homogen);
086    if (homogen) {
087      if (!rowHeads.equals(colHeads))
088        throw new NoHomogenRelationException();
089      this.rowHeads = rowHeads;
090      this.colHeads = (SetList<C>) this.rowHeads;
091    } else {
092      this.rowHeads = rowHeads;
093      this.colHeads = colHeads;
094    }
095  }
096
097  protected AbstractRelation(final boolean homogen) {
098    super();
099    this.homogen = homogen;
100  }
101
102  public SetList<R> rowHeads() {
103    return rowHeads;
104  }
105
106  public SetList<C> colHeads() {
107    return colHeads;
108  }
109
110  public abstract boolean contains(Object o1, Object o2);
111
112  public boolean containsAll(final Relation<?, ?> r) {
113    for (Pair<?, ?> p : r)
114      if (!contains(p.x(), p.y()))
115        return false;
116    return true;
117  }
118
119  public Set<C> row(final Object o) {
120    return rowAnd(o);
121  }
122
123  public Set<R> col(final Object o) {
124    return colAnd(o);
125  }
126
127  public final Set<C> rowAnd(final Object... rows) {
128    return rowAnd(Arrays.asList(rows));
129  }
130
131  public final Set<R> colAnd(final Object... cols) {
132    return colAnd(Arrays.asList(cols));
133  }
134
135  public Set<C> rowAnd(final Collection<?> rows) {
136    return Sets.filter(colHeads(), new Predicate<C>() {
137
138      public final boolean apply(C col) {
139        for (Object row : rows)
140          if (!AbstractRelation.this.contains(row, col))
141            return false;
142        return true;
143      }
144    });
145  }
146
147  public Set<R> colAnd(final Collection<?> cols) {
148    return Sets.filter(rowHeads(), new Predicate<R>() {
149
150      public final boolean apply(final R row) {
151        for (Object col : cols)
152          if (!AbstractRelation.this.contains(row, col))
153            return false;
154        return true;
155      }
156    });
157  }
158
159  public Relation<R, C> subRelation(final Collection<?> rows, final Collection<?> cols) {
160    return new AbstractRelation<R, C>(
161        SetLists.intersection(rowHeads, rows),
162        SetLists.intersection(colHeads, cols),
163        false) {
164
165      public final boolean contains(final Object o1, final Object o2) {
166        return rowHeads().contains(o1) && colHeads().contains(o2) && AbstractRelation.this.contains(o1, o2);
167      }
168    };
169  }
170
171  public Relation<R, C> filter(
172      final Predicate<? super R> rowPredicate,
173      final Predicate<? super C> colPredicate,
174      final Predicate<Pair<R, C>> relationPredicate) {
175    return new AbstractRelation<R, C>(rowHeads.filter(rowPredicate), colHeads.filter(colPredicate), false) {
176
177      @SuppressWarnings("unchecked")
178      public final boolean contains(final Object o1, final Object o2) {
179        return rowHeads().contains(o1) && colHeads().contains(o2) && AbstractRelation.this.contains(o1, o2)
180            && relationPredicate.apply(new Pair<R, C>((R) o1, (C) o2));
181      }
182    };
183  }
184
185  public boolean equals(final Object o) {
186    return this == o
187        || (o instanceof Relation && size() == ((Relation<?, ?>) o).size() && containsAll((Relation<?, ?>) o));
188  }
189
190  public final boolean smallerEq(final Relation<R, C> r) {
191    return r.containsAll(this);
192  }
193
194  public final boolean smaller(final Relation<R, C> r) {
195    return size() < r.size() && smallerEq(r);
196  }
197
198  public final boolean greaterEq(final Relation<R, C> r) {
199    return r.smallerEq(this);
200  }
201
202  public final boolean greater(final Relation<R, C> r) {
203    return r.smaller(this);
204  }
205
206  public final boolean uncomparable(final Relation<R, C> r) {
207    return !smallerEq(r) && !greaterEq(r);
208  }
209
210  public final int compareTo(final Relation<R, C> r) {
211    if (equals(r))
212      return 0;
213    if (smallerEq(r))
214      return -1;
215    if (greaterEq(r))
216      return 1;
217    return Integer.MAX_VALUE;
218  }
219
220  public Iterator<Pair<R, C>> iterator() {
221    return Iterators.filter(
222        ListIterators.<R, C> cartesianProduct(rowHeads().listIterator(), colHeads().listIterator(), 0),
223        new Predicate<Pair<R, C>>() {
224
225          public final boolean apply(final Pair<R, C> p) {
226            return contains(p.x(), p.y());
227          }
228        });
229  }
230
231  public int size() {
232    int size = 0;
233    for (R row : rowHeads())
234      for (C col : colHeads())
235        if (contains(row, col))
236          size++;
237    return size;
238  }
239
240  public boolean isFull() {
241    for (R row : rowHeads())
242      for (C col : colHeads())
243        if (!contains(row, col))
244          return false;
245    return true;
246  }
247
248  public boolean isEmpty() {
249    for (R row : rowHeads())
250      for (C col : colHeads())
251        if (contains(row, col))
252          return false;
253    return true;
254  }
255
256  public MatrixRelation<R, C> clone() {
257    final MatrixRelation<R, C> clone = new MatrixRelation<R, C>(rowHeads(), colHeads(), homogen);
258    clone.addAllFast(this);
259    return clone;
260  }
261
262  public boolean[][] toArray() {
263    final boolean[][] a = new boolean[rowHeads().size()][colHeads().size()];
264    for (int i = 0; i < rowHeads().size(); i++)
265      for (int j = 0; j < colHeads().size(); j++)
266        a[i][j] = contains(rowHeads().get(i), colHeads().get(j));
267    return a;
268  }
269
270  private static final int colspan = 5;
271
272  public String toString() {
273    final StringBuilder s = new StringBuilder();
274    s.append(getClass().getName() + "@" + hashCode() + "\r\n");
275    s.append(rowHeads().size() + " domain elements.\r\n");
276    s.append(colHeads().size() + " codomain elements.\r\n");
277    String spaces = "";
278    while (spaces.length() < colspan)
279      spaces += " ";
280    s.append(spaces);
281    spaces = spaces.substring(1);
282    String c;
283    for (C col : colHeads()) {
284      c = col.toString().substring(0, Math.min(colspan, col.toString().length()));
285      while (c.length() < colspan)
286        c += " ";
287      s.append("\t" + c);
288    }
289    s.append("\r\n");
290    String r;
291    for (R row : rowHeads()) {
292      r = row.toString().substring(0, Math.min(colspan, row.toString().length()));
293      while (r.length() < colspan)
294        r += " ";
295      s.append(r);
296      for (C col : colHeads())
297        if (contains(row, col))
298          s.append("\tX" + spaces);
299        else
300          s.append("\t." + spaces);
301      s.append("\r\n");
302    }
303    return s.toString();
304  }
305
306  public void empty() {
307    throw new UnsupportedOperationException();
308  }
309
310  public void fill() {
311    throw new UnsupportedOperationException();
312  }
313
314  public void dispose() {
315    throw new UnsupportedOperationException();
316  }
317
318  public boolean add(final R row, final C col) {
319    throw new UnsupportedOperationException();
320  }
321
322  public boolean addFast(final Object o1, final Object o2) {
323    throw new UnsupportedOperationException();
324  }
325
326  public boolean addAll(final Relation<? extends R, ? extends C> r) {
327    throw new UnsupportedOperationException();
328  }
329
330  public boolean addAllFast(final Relation<?, ?> r) {
331    throw new UnsupportedOperationException();
332  }
333
334  public boolean remove(final Object o1, final Object o2) {
335    throw new UnsupportedOperationException();
336  }
337
338  public boolean removeAll(final Relation<?, ?> r) {
339    throw new UnsupportedOperationException();
340  }
341
342  public boolean retainAll(final Relation<?, ?> r) {
343    throw new UnsupportedOperationException();
344  }
345
346  @Override
347  public boolean isHomogen() {
348    return homogen;
349  }
350
351  protected final void checkHomogen() throws NoHomogenRelationException {
352    if (!isHomogen())
353      throw new NoHomogenRelationException();
354  }
355
356  public MatrixRelation<R, R> neighborhood() {
357    checkHomogen();
358    return clone().neighborhood();
359  }
360
361  public MatrixRelation<R, R> order() {
362    checkHomogen();
363    return clone().order();
364  }
365
366  public SetList<Set<R>> equivalenceClasses() {
367    checkHomogen();
368    return new AbstractSetList<Set<R>>() {
369
370      @Override
371      public final ListIterator<Set<R>> listIterator(final int i) {
372        return new SimpleListIterator<Set<R>>(true) {
373
374          private final HashSet<R> available = new HashSet<R>(rowHeads());
375
376          {
377            createFirst(i);
378          }
379
380          @Override
381          protected final Set<R> createNext() {
382            try {
383              final R head = Collections3.firstElement(available);
384              final Set<R> eq = new HashSet<R>(col(head));
385              available.removeAll(eq);
386              return eq;
387            } catch (NoSuchElementException e) {
388              return null;
389            }
390          }
391
392          @Override
393          protected final Set<R> createPrevious() {
394            // TODO Auto-generated method stub
395            return null;
396          }
397        };
398      }
399    };
400  }
401}