LongRangeDistribution.java
/*******************************************************************************
* Copyright (c) 2021 Handy Tools for Distributed Computing (HanDist) project.
*
* This program and the accompanying materials are made available to you under
* the terms of the Eclipse Public License 1.0 which accompanies this
* distribution,
* and is available at https://www.eclipse.org/legal/epl-v10.html
*
* SPDX-License-Identifier: EPL-1.0
******************************************************************************/
package handist.collections.dist;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import apgas.Place;
import handist.collections.ChunkedList;
import handist.collections.ChunkedList.LongRangeOrdering;
import handist.collections.LongRange;
/**
* Class tracking the distribution of a collection whose entries are identified
* by {@link LongRange}. Currently, this is limited to class {@link DistCol} but
* may be extended to other distributed collections in the future.
* <p>
* This class allows users to inquire about the location of particular entries
* of a distributed collection through methods
* {@link #rangeLocation(LongRange)}, {@link #location(Long)}, and
* {@link #location(LongRange)}. Internally, this information is kept in a
* {@link Map}<{@link LongRange},{@link Place}>.
* <p>
* This class is an {@link UpdatableDistribution}, meaning it can be registered
* into a collection so that it is automatically updated when changes in the
* distribution occur, i.e. using
* {@link DistCol#registerDistribution(UpdatableDistribution)}. It may even be
* safely registered into multiple distributed collections provided that the
* collections do not share any {@link LongRange} "keys".
* <p>
* To maintain the integrity of the distribution with respect to the underlying
* distributed collection(s), this object cannot be modified by the user.
*
* @author Patrick Finnerty
*
*/
public class LongRangeDistribution extends UpdatableDistribution<LongRange>
implements RangedDistribution<LongRange>, Distribution<Long> {
/** Serial Version UID */
private static final long serialVersionUID = 2646369287127470136L;
/**
* Internal Sorted Map used to keep track of the location of each chunk of the
* underlying {@link DistCol}.
*/
private final TreeMap<LongRange, Place> distribution;
/**
* Default constructor. Creates a blank distribution which may be registered
* into a distributed collection to track its distribution.
*/
public LongRangeDistribution() {
distribution = new TreeMap<>(new ChunkedList.LongRangeOrdering());
}
/**
*
* @param map map from {@link LongRange} to {@link Place} describing a
* distribution
*/
public LongRangeDistribution(Map<LongRange, Place> map) {
distribution = new TreeMap<>(new LongRangeOrdering());
distribution.putAll(map);
}
/**
* Constructor for internal use only.
*
* Creates a new {@link LongRangeDistribution} with the provided
* {@link TreeMap}. It is assumed that the provided map uses the
* {@link LongRangeOrdering} to sort its keys. If it is not the case, this
* constructor will throw an {@link IllegalArgumentException}.
*
*
* @param map the map to use to build this instance
* @throws IllegalArgumentException if the provided map does not use the
* appropriate ordering
* @see #clone()
*/
protected LongRangeDistribution(TreeMap<LongRange, Place> map) {
if (!(map.comparator() instanceof LongRangeOrdering)) {
throw new IllegalArgumentException(
"The provided map should use ChunkedList.LongRangeOrdering to sort its keys");
}
distribution = map;
}
/**
* Returns a copy of this distribution by allocating a new
* {@link LongRangeDistribution} object. Any subsequent changes made to this
* object will NOT be reflected into the returned copy and vice-versa.
*/
@SuppressWarnings("unchecked")
@Override
public LongRangeDistribution clone() {
return new LongRangeDistribution((TreeMap<LongRange, Place>) distribution.clone());
}
/**
* Creates a copy of the current distribution and returns it as a map from
* {@link LongRange} to {@link Place}. The returned map may be modified freely
* without any adverse influence on the integrity of this instance.
*
* @return a copy of the map used internally to keep track of the distribution
*/
@SuppressWarnings("unchecked")
public Map<LongRange, Place> getDistribution() {
return (Map<LongRange, Place>) distribution.clone();
}
@Override
public Place location(Long key) {
final LongRange lr = new LongRange(key);
final Map.Entry<LongRange, Place> entry = distribution.floorEntry(lr);
if (entry == null || !entry.getKey().contains(key)) {
throw new IndexOutOfBoundsException(
"LongRangeDistribution (as LongDistribution): " + key + " is not within the range of any chunk");
}
return entry.getValue();
}
/**
* Provides the location of the specified range. It is assumed that the
* specified range is exactly contained in this distribution. If the targeted
* range does not exist in the collection, or if it has been split into 2 or
* more chunks, this method will return null.
* <p>
* In cases where a range may have been split into several pieces, consider
* using method {@link #rangeLocation(LongRange)} to obtain the location of each
* piece.
*
* @param lr the range whose location needs to be returned
* @return the location of the specified range, or null is this exact range is
* not contained in the distributed collection
*/
public Place location(LongRange lr) {
return distribution.get(lr);
}
@Override
public Map<LongRange, Place> rangeLocation(LongRange range) {
final Map<LongRange, Place> listPlaceRange = new HashMap<>();
for (final LongRange mappedRange : distribution.keySet()) {
final Place mappedPlace = distribution.get(mappedRange);
if (mappedRange.from <= range.from) {
if (range.from < mappedRange.to) { // if (range.min <= mappedRange.max) {
if (range.to <= mappedRange.to) {
listPlaceRange.put(range, mappedPlace);
} else {
listPlaceRange.put(new LongRange(range.from, mappedRange.to), mappedPlace);
}
}
} else {
if (mappedRange.from < range.to) { // if (mappedRange.min <= range.max) {
if (range.to <= mappedRange.to) {
listPlaceRange.put(new LongRange(mappedRange.from, range.to), mappedPlace);
} else {
listPlaceRange.put(mappedRange, mappedPlace);
}
}
}
}
return listPlaceRange;
}
@Override
void removeLocation(LongRange k) {
distribution.remove(k);
}
@Override
void updateLocation(LongRange k, Place location) {
distribution.put(k, location);
}
}