cdc.applic.expressions.content.AbstractRangeSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cdc-applic-expressions Show documentation
Show all versions of cdc-applic-expressions Show documentation
Applicabilities Expressions.
package cdc.applic.expressions.content;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import cdc.graphs.PartialOrderPosition;
import cdc.util.lang.Checks;
import cdc.util.lang.CollectionUtils;
/**
* Base abstract class of sets based on {@link Domain}.
*
* @author Damien Carbonne
* @param The value type.
* @param The range type.
* @param The set type.
*/
public abstract class AbstractRangeSet,
R extends AbstractRange,
S extends AbstractRangeSet>
extends AbstractSItemSet implements CheckedSet, Comparable {
/** Sorted non empty ranges. */
private final List ranges;
/** The associated domain. */
private final Domain domain;
protected abstract S create(List ranges);
/**
* @return The empty set.
*/
public abstract S empty();
/**
* @return This set.
*/
protected abstract S self();
/**
* Converts an {@link SItemSet} as a {@code S} set.
*
* @param other The set to convert.
* @return The conversion of {@code other} as an {@code S} set.
*/
protected abstract S adapt(SItemSet other);
protected AbstractRangeSet(Domain domain) {
this.domain = domain;
this.ranges = Collections.emptyList();
}
protected AbstractRangeSet(Domain domain,
Collection ranges) {
this.domain = domain;
if (ranges.isEmpty()) {
this.ranges = Collections.emptyList();
} else {
final List list = new ArrayList<>();
for (final R range : ranges) {
unionInternal(list, range);
}
this.ranges = Collections.unmodifiableList(list);
}
}
// /**
// * Checks that a list of ranges is sorted.
// *
// * @param list The list of ranges to check.
// */
// private void check(List list) {
// if (list.size() > 1) {
// for (int index = 0; index < list.size() - 1; index++) {
// final R r1 = list.get(index);
// final R r2 = list.get(index + 1);
// Checks.assertTrue(r1.compareTo(r2) < 0, "Implementation error (" + list + ")");
// }
// }
// }
private int binarySearch(List ranges,
V value) {
return Collections.binarySearch(ranges, domain.create(value));
}
/**
* Adds a range (possibly empty) to a sorted list (possibly empty) of ranges.
*
* @param sorted The sorted list of ranges.
* @param range The range to add.
*/
private void unionInternal(List sorted,
R range) {
if (!range.isEmpty()) {
if (sorted.isEmpty()) {
sorted.add(range);
} else {
final int size = sorted.size();
// Index of the range that contains min
final int first = binarySearch(sorted, range.getMin());
if (first < 0
&& -(first + 1) == size
&& !domain.adjoint(sorted.get(size - 1).getMax(), range.getMin())) {
// 1) No range contains min
// 2) [min~min] should be created in last position
// 3) min is not joined with last range
// Conclusion: New range is strictly after all ranges
sorted.add(range);
} else {
// Index of the range that contains max
final int last = binarySearch(sorted, range.getMax());
if (last < 0
&& -(last + 1) == 0
&& !domain.adjoint(sorted.get(0).getMin(), range.getMax())) {
// 1) No range contains max
// 2) [max~max] should be inserted in first position
// 3) max is not joined with first range
// Conclusion: New range is strictly before all ranges
sorted.add(0, range);
} else {
// New range is overlapping / joining existing ranges
// Index of the first merged range
final int efirst;
if (first >= 0) {
// Range that contains min exists
efirst = first;
} else {
// Range that contains min does not exist
final int index = -(first + 1);
if (index > 0 && domain.adjoint(sorted.get(index - 1).getMax(), range.getMin())) {
efirst = index - 1;
} else {
efirst = index;
}
}
// Index of the last merged range
final int elast;
if (last >= 0) {
elast = last;
} else {
final int index = -(last + 1);
if (index < size && domain.adjoint(sorted.get(index).getMin(), range.getMax())) {
elast = index;
} else {
elast = index - 1;
}
}
// All existing ranges between efirst (inclusive) and
// elast (inclusive) must be merged into one range
// and replaced by this merged range
R merged = range;
for (int index = efirst; index <= elast; index++) {
merged = RangeUtils.union(domain, merged, sorted.get(index));
}
sorted.subList(efirst, elast + 1).clear();
sorted.add(efirst, merged);
}
}
}
// check(sorted);
}
}
/**
* Removes a non empty range from a non empty sorted list of ranges.
*
* @param sorted The sorted list of ranges.
* @param range The range to remove.
*/
private void removeInternal(List sorted,
R range) {
// Checks.assertTrue(!sorted.isEmpty(), "sorted is empty");
// Checks.assertTrue(!range.isEmpty(), "range is empty");
final int size = sorted.size();
// Index of the range that contains min
final int first = binarySearch(sorted, range.getMin());
if (first < 0
&& -(first + 1) == size) {
// 1) No range contains min
// 2) [min~min] should be created in last position
// Conclusion: Nothing to remove
} else {
// Index of the range that contains max
final int last = binarySearch(sorted, range.getMax());
if (last < 0
&& -(last + 1) == 0) {
// 1) No range contains max
// 2) [max~max] should be inserted in first position
// Conclusion: New range is strictly before all ranges
} else {
// range is overlapping / joining existing ranges
// Index of the first modified range
final int efirst;
if (first >= 0) {
// Range that contains min exists
efirst = first;
} else {
// Range that contains min does not exist
final int index = -(first + 1);
efirst = index;
}
// Index of the last modified range
final int elast;
if (last >= 0) {
elast = last;
} else {
final int index = -(last + 1);
elast = index - 1;
}
if (efirst == elast) {
final R[] tmp = RangeUtils.remove(domain, sorted.get(efirst), range);
if (tmp.length == 0) {
sorted.remove(efirst);
} else if (tmp.length == 1) {
sorted.set(efirst, tmp[0]);
} else {
sorted.set(efirst, tmp[0]);
sorted.add(efirst + 1, tmp[1]);
}
} else if (efirst < elast) {
sorted.set(efirst, RangeUtils.removeSimple(domain, sorted.get(efirst), range));
sorted.set(elast, RangeUtils.removeSimple(domain, sorted.get(elast), range));
if (elast - efirst > 1) {
sorted.subList(efirst + 1, elast).clear();
}
if (sorted.get(efirst + 1).isEmpty()) {
sorted.remove(efirst + 1);
}
if (sorted.get(efirst).isEmpty()) {
sorted.remove(efirst);
}
}
// check(sorted);
}
}
}
/**
* Computes the intersection of a non empty range with a non empty sorted list of ranges
* and adds the result to another sorted list (possibly empty) of ranges.
*
* @param sorted The sorted list to when intersection is added.
* @param ranges The sorted list of ranges with which intersection is computed.
* @param range The range with which intersection is computed.
*/
private void intersectionInternal(List sorted,
List ranges,
R range) {
// Checks.assertTrue(!ranges.isEmpty(), "ranges is empty");
// Checks.assertTrue(!range.isEmpty(), "range is empty");
final int size = ranges.size();
// Index of the range that contains min
final int first = binarySearch(ranges, range.getMin());
if (first < 0
&& -(first + 1) == size) {
// 1) No range contains min
// 2) [min~min] should be created in last position
// Conclusion: Empty intersection
} else {
// Index of the range that contains max
final int last = binarySearch(ranges, range.getMax());
if (last < 0
&& -(last + 1) == 0) {
// 1) No range contains max
// 2) [max~max] should be inserted in first position
// Conclusion: empty intersection
} else {
// range is overlapping / joining existing ranges
// Index of the first intersecting range
final int efirst;
if (first >= 0) {
// Range that contains min exists
efirst = first;
} else {
// Range that contains min does not exist
final int index = -(first + 1);
efirst = index;
}
// Index of the last intersecting range
final int elast;
if (last >= 0) {
elast = last;
} else {
final int index = -(last + 1);
elast = index - 1;
}
if (efirst == elast) {
final R r = RangeUtils.intersection(domain, ranges.get(efirst), range);
unionInternal(sorted, r);
} else if (efirst < elast) {
final R f = RangeUtils.intersection(domain, ranges.get(efirst), range);
unionInternal(sorted, f);
final R l = RangeUtils.intersection(domain, ranges.get(elast), range);
unionInternal(sorted, l);
// All ranges between efirst (exclusive) and elast (exclusive)
// belong to intersection.
for (int index = efirst + 1; index < elast; index++) {
unionInternal(sorted, ranges.get(index));
}
}
// check(sorted);
}
}
}
public final List getRanges() {
return ranges;
}
@Override
public final boolean isEmpty() {
return ranges.isEmpty();
}
@Override
public final boolean containsRanges() {
for (final R range : ranges) {
if (!range.isSingleton()) {
return true;
}
}
return false;
}
@Override
public final boolean isSingleton() {
return ranges.size() == 1 && ranges.get(0).isSingleton();
}
@Override
public V getSingletonValue() {
checkIsSingleton();
return ranges.get(0).getSingletonValue();
}
@Override
public final boolean contains(V value) {
return binarySearch(ranges, value) >= 0;
}
@Override
public final boolean contains(S set) {
Checks.isNotNull(set, SET);
if (set.isEmpty()) {
return true;
} else {
for (final R range : set.getRanges()) {
if (!contains(range)) {
return false;
}
}
return true;
}
}
public final boolean contains(R range) {
if (range.isEmpty()) {
return true;
} else {
final int first = binarySearch(ranges, range.getMin());
final int last = binarySearch(ranges, range.getMax());
return first == last && first >= 0;
}
}
@Override
public final S union(V value) {
if (contains(value)) {
return self();
} else {
final List sorted = new ArrayList<>(this.ranges);
unionInternal(sorted, domain.create(value));
return create(sorted);
}
}
public final S union(R range) {
if (contains(range)) {
return self();
} else {
final List sorted = new ArrayList<>(this.ranges);
unionInternal(sorted, range);
return create(sorted);
}
}
@Override
public final S union(S set) {
if (set.isEmpty()) {
return self();
} else {
final List sorted = new ArrayList<>(this.ranges);
for (final R range : set.getRanges()) {
unionInternal(sorted, range);
}
return create(sorted);
}
}
@Override
public final S remove(V value) {
if (!contains(value)) {
return self();
} else {
final List sorted = new ArrayList<>(this.ranges);
removeInternal(sorted, domain.create(value));
return create(sorted);
}
}
public final S remove(R range) {
if (isEmpty() || range.isEmpty()) {
return self();
} else {
final List sorted = new ArrayList<>(this.ranges);
removeInternal(sorted, range);
return create(sorted);
}
}
@Override
public final S remove(S set) {
if (isEmpty() || set.isEmpty()) {
return self();
} else {
final List sorted = new ArrayList<>(this.ranges);
for (final R range : set.getRanges()) {
removeInternal(sorted, range);
}
return create(sorted);
}
}
@Override
public final S intersection(V value) {
if (isEmpty()) {
return self();
} else {
final List sorted = new ArrayList<>();
if (contains(value)) {
sorted.add(domain.create(value));
}
return create(sorted);
}
}
public final S intersection(R range) {
if (isEmpty()) {
return self();
} else {
final List sorted = new ArrayList<>();
intersectionInternal(sorted, ranges, range);
return create(sorted);
}
}
@Override
public final S intersection(S set) {
if (isEmpty()) {
return self();
} else if (set.isEmpty()) {
return set;
} else {
final List sorted = new ArrayList<>();
for (final R range : set.getRanges()) {
intersectionInternal(sorted, ranges, range);
}
return create(sorted);
}
}
@Override
public String getContent() {
final StringBuilder builder = new StringBuilder();
boolean first = true;
for (final R range : getRanges()) {
if (!first) {
builder.append(",");
}
builder.append(range.toString());
first = false;
}
return builder.toString();
}
@Override
public final boolean isChecked() {
return true;
}
@Override
public final S getChecked() {
return self();
}
@Override
public final S getBest() {
return self();
}
@Override
public final boolean isValid() {
return true;
}
@Override
public List getItems() {
return getRanges();
}
@Override
public final boolean contains(SItem item) {
Checks.isNotNull(item, ITEM);
if (domain.getValueClass().isInstance(item)) {
return contains(domain.getValueClass().cast(item));
} else if (domain.getRangeClass().isInstance(item)) {
return contains(domain.getRangeClass().cast(item));
} else {
throw nonSupportedItem(item);
}
}
@Override
public final S union(SItem item) {
Checks.isNotNull(item, ITEM);
if (domain.getValueClass().isInstance(item)) {
return union(domain.getValueClass().cast(item));
} else if (domain.getRangeClass().isInstance(item)) {
return union(domain.getRangeClass().cast(item));
} else {
throw nonSupportedItem(item);
}
}
@Override
public final S intersection(SItem item) {
Checks.isNotNull(item, ITEM);
if (domain.getValueClass().isInstance(item)) {
return intersection(domain.getValueClass().cast(item));
} else if (domain.getRangeClass().isInstance(item)) {
return intersection(domain.getRangeClass().cast(item));
} else {
throw nonSupportedItem(item);
}
}
@Override
public final S remove(SItem item) {
Checks.isNotNull(item, ITEM);
if (domain.getValueClass().isInstance(item)) {
return remove(domain.getValueClass().cast(item));
} else if (domain.getRangeClass().isInstance(item)) {
return remove(domain.getRangeClass().cast(item));
} else {
throw nonSupportedItem(item);
}
}
@Override
public final S union(SItemSet set) {
Checks.isNotNull(set, SET);
if (set.isCompliantWith(getClass())) {
return union(adapt(set));
} else {
throw nonSupportedSet(set);
}
}
@Override
public final S intersection(SItemSet set) {
Checks.isNotNull(set, SET);
if (set.isCompliantWith(getClass())) {
return intersection(adapt(set));
} else {
throw nonSupportedSet(set);
}
}
@Override
public final S remove(SItemSet set) {
Checks.isNotNull(set, SET);
if (set.isCompliantWith(getClass())) {
return remove(adapt(set));
} else {
throw nonSupportedSet(set);
}
}
protected static ,
V extends Value & Comparable super V>,
R extends AbstractRange,
S extends AbstractRangeSet>
S toSet(D domain,
S empty,
Function create,
V value,
PartialOrderPosition position) {
Checks.isNotNull(value, VALUE);
Checks.isNotNull(position, "position");
if (position == PartialOrderPosition.EQUAL) {
return create.apply(domain.create(value));
} else if (position == PartialOrderPosition.UNRELATED) {
return empty;
} else if (position == PartialOrderPosition.LESS_THAN) {
return create.apply(domain.create(domain.min(), domain.pred(value)));
} else {
return create.apply(domain.create(domain.succ(value), domain.max()));
}
}
public static ,
R extends AbstractRange,
S extends AbstractRangeSet>
Comparator lexicographicComparator() {
return (s1,
s2) -> {
if (s1 == s2) {
return 0;
}
if (s1 == null || s2 == null) {
return s1 == null ? -1 : 1;
}
return CollectionUtils.compareLexicographic(s1.getRanges(),
s2.getRanges(),
AbstractRange.minMaxComparator());
};
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (isEmpty() && object instanceof AbstractRangeSet) {
final AbstractRangeSet, ?, ?> other = (AbstractRangeSet, ?, ?>) object;
if (other.isEmpty()) {
return true;
}
}
if (!(object instanceof AbstractRangeSet)) {
return false;
}
final AbstractRangeSet, ?, ?> other = (AbstractRangeSet, ?, ?>) object;
return ranges.equals(other.ranges);
}
@Override
public int hashCode() {
return isEmpty() ? 0 : ranges.hashCode();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("{");
builder.append(getContent());
builder.append("}");
return builder.toString();
}
@Override
public int compareTo(S o) {
if (this == o) {
return 0;
}
if (o == null) {
return 1;
} else {
return CollectionUtils.compareLexicographic(getItems(), o.getItems());
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy