All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.ma2.Range Maven / Gradle / Ivy

Go to download

The NetCDF-Java Library is a Java interface to NetCDF files, as well as to many other types of scientific data formats.

The newest version!
/*
 * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
 *
 * Portions of this software were developed by the Unidata Program at the
 * University Corporation for Atmospheric Research.
 *
 * Access and use of this software shall impose the following obligations
 * and understandings on the user. The user is granted the right, without
 * any fee or cost, to use, copy, modify, alter, enhance and distribute
 * this software, and any derivative works thereof, and its supporting
 * documentation for any purpose whatsoever, provided that this entire
 * notice appears in all copies of the software, derivative works and
 * supporting documentation.  Further, UCAR requests that the user credit
 * UCAR/Unidata in any publications that result from the use of this
 * software or in any product that includes this software. The names UCAR
 * and/or Unidata, however, may not be used in any advertising or publicity
 * to endorse or promote any products or commercial entity unless specific
 * written permission is obtained from UCAR/Unidata. The user also
 * understands that UCAR/Unidata is not obligated to provide the user with
 * any support, consulting, training or assistance of any kind with regard
 * to the use, operation and performance of this software nor to provide
 * the user with any updates, revisions, new versions or "bug fixes."
 *
 * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package ucar.ma2;

import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;


/**
 * Represents a set of integers, used as an index for arrays.
 * It should be considered as a subset of the interval of integers [0, length-1] inclusive.
 * For example Range(1:11:3) represents the set of integers {1,4,7,10}
 * Immutable.
 * 

* Ranges are monotonically increasing. * Elements must be nonnegative. * EMPTY is the empty Range. * VLEN is for variable length dimensions. *

Note last is inclusive, so standard iteration is *

 * for (int i=range.first(); i<=range.last(); i+= range.stride()) {
 *   ...
 * }
 * or use:
 * Range.Iterator iter = timeRange.getIterator();
 * while (iter.hasNext()) {
 *   int index = iter.next();
 *   ...
 * }
 * 
* * @author caron */ public final class Range { public static final Range EMPTY = new Range(); public static final Range VLEN = new Range(-1); private final int n; // number of elements private final int first; // first value in range private final int stride; // stride, must be >= 1 private final String name; // optional name /** * Used for EMPTY */ private Range() { this.n = 0; this.first = 0; this.stride = 1; this.name = null; } /** * Create a range with unit stride. * * @param first first value in range * @param last last value in range, inclusive * @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last */ public Range(int first, int last) throws InvalidRangeException { this(null, first, last, 1); } /** * Create a range starting at zero, with unit stride. * @param length number of elements in the Rnage */ public Range(int length) { this.name = null; this.first = 0; this.stride = 1; this.n = length; } /** * Create a named range with unit stride. * * @param name name of Range * @param first first value in range * @param last last value in range, inclusive * @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last */ public Range(String name, int first, int last) throws InvalidRangeException { this(name, first, last, 1); } /** * Create a range with a specified stride. * * @param first first value in range * @param last last value in range, inclusive * @param stride stride between consecutive elements, must be > 0 * @throws InvalidRangeException elements must be nonnegative: 0 <= first <= last, stride > 0 */ public Range(int first, int last, int stride) throws InvalidRangeException { this(null, first, last, stride); } /** * Create a named range with a specified stride. * * @param name name of Range * @param first first value in range * @param last last value in range, inclusive * @param stride stride between consecutive elements, must be > 0 * @throws InvalidRangeException elements must be nonnegative: 0 <= first <= last, stride > 0 */ public Range(String name, int first, int last, int stride) throws InvalidRangeException { if (first < 0) throw new InvalidRangeException("first ("+first+") must be >= 0"); if (last < first) throw new InvalidRangeException("last ("+last+") must be >= first ("+first+")"); if (stride < 1) throw new InvalidRangeException("stride ("+stride+") must be > 0"); this.name = name; this.first = first; this.stride = stride; this.n = Math.max(1 + (last - first) / stride, 1); } /** * Copy Constructor * * @param r copy from here */ public Range(Range r) { first = r.first(); n = r.length(); stride = r.stride(); name = r.getName(); } /** * Copy Constructor with name * * @param name result name * @param r copy from here */ public Range(String name, Range r) { this.name = name; first = r.first(); n = r.length(); stride = r.stride(); } /** * Create a new Range by composing a Range that is reletive to this Range. * Revised 2013/04/19 by Dennis Heimbigner to handle edge cases. * See the commentary associated with the netcdf-c file dceconstraints.h, * function dceslicecompose(). * * @param r range reletive to base * @return combined Range, may be EMPTY * @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last */ public Range compose(Range r) throws InvalidRangeException { if ((length() == 0) || (r.length() == 0)) return EMPTY; if (this == VLEN || r == VLEN) return VLEN; if(false) {// Original version // Note that this version assumes that range r is // correct with respect to this. int first = element(r.first()); int stride = stride() * r.stride(); int last = element(r.last()); return new Range(name, first, last, stride); } else {//new version: handles versions all values of r. int sr_stride = stride() * r.stride(); int sr_first = element(r.first()); // MAP(this,i) == element(i) int lastx = element(r.last()); int sr_last = (last() < lastx ? last() : lastx); //min(last(),lastx) //unused int sr_length = (sr_last + 1) - sr_first; return new Range(name, sr_first, sr_last, sr_stride); } } /** * Create a new Range by compacting this Range by removing the stride. * first = first/stride, last=last/stride, stride=1. * * @return compacted Range * @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last */ public Range compact() throws InvalidRangeException { if (stride() == 1) return this; int first = first() / stride(); int last = first + length() - 1; return new Range(name, first, last, 1); } /** * Create a new Range shifting this range by a constant factor. * * @param origin subtract this from first, last * @return shiften range * @throws InvalidRangeException elements must be nonnegative, 0 <= first <= last */ public Range shiftOrigin(int origin) throws InvalidRangeException { if (this == VLEN) return VLEN; int first = first() - origin; int stride = stride(); int last = last() - origin; return new Range(name, first, last, stride); } /** * Create a new Range by intersecting with a Range using same interval as this Range. * NOTE: we dont yet support intersection when both Ranges have strides * * @param r range to intersect * @return intersected Range, may be EMPTY * @throws InvalidRangeException elements must be nonnegative */ public Range intersect(Range r) throws InvalidRangeException { if ((length() == 0) || (r.length() == 0)) return EMPTY; if (this == VLEN || r == VLEN) return VLEN; int last = Math.min(this.last(), r.last()); int stride = stride() * r.stride(); int useFirst; if (stride == 1) { useFirst = Math.max(this.first(), r.first()); } else if (stride() == 1) { // then r has a stride if (r.first() >= first()) useFirst = r.first(); else { int incr = (first() - r.first()) / stride; useFirst = r.first() + incr * stride; if (useFirst < first()) useFirst += stride; } } else if (r.stride() == 1) { // then this has a stride if (first() >= r.first()) useFirst = first(); else { int incr = (r.first() - first()) / stride; useFirst = first() + incr * stride; if (useFirst < r.first()) useFirst += stride; } } else { throw new UnsupportedOperationException("Intersection when both ranges have a stride"); } if (useFirst > last) return EMPTY; return new Range(name, useFirst, last, stride); } /** * Determine if a given Range intersects this one. * NOTE: we dont yet support intersection when both Ranges have strides * * @param r range to intersect * @return true if they intersect * @throws UnsupportedOperationException if both Ranges have strides */ public boolean intersects(Range r) { if ((length() == 0) || (r.length() == 0)) return false; if (this == VLEN || r == VLEN) return true; int last = Math.min(this.last(), r.last()); int stride = stride() * r.stride(); int useFirst; if (stride == 1) { useFirst = Math.max(this.first(), r.first()); } else if (stride() == 1) { // then r has a stride if (r.first() >= first()) useFirst = r.first(); else { int incr = (first() - r.first()) / stride; useFirst = r.first() + incr * stride; if (useFirst < first()) useFirst += stride; } } else if (r.stride() == 1) { // then this has a stride if (first() >= r.first()) useFirst = first(); else { int incr = (r.first() - first()) / stride; useFirst = first() + incr * stride; if (useFirst < r.first()) useFirst += stride; } } else { throw new UnsupportedOperationException("Intersection when both ranges have a stride"); } return (useFirst <= last); } /** * If this range is completely past the wanted range * @param want desired range * @return true if first() > want.last() */ public boolean past(Range want) { return (first() > want.last()); } /** * Create a new Range by making the union with a Range using same interval as this Range. * NOTE: no strides * * @param r range to add * @return intersected Range, may be EMPTY * @throws InvalidRangeException elements must be nonnegative */ public Range union(Range r) throws InvalidRangeException { if (length() == 0) return r; if (this == VLEN || r == VLEN) return VLEN; if (r.length() == 0) return this; int first = Math.min(this.first(), r.first()); int last = Math.max(this.last(), r.last()); return new Range(name, first, last); } /** * Get the number of elements in the range. * @return the number of elements in the range. */ public int length() { return n; } /** * Get ith element * * @param i index of the element * @return the i-th element of a range. * @throws InvalidRangeException i must be: 0 <= i < length */ public int element(int i) throws InvalidRangeException { if (i < 0) throw new InvalidRangeException("i must be >= 0"); if (i >= n) throw new InvalidRangeException("i must be < length"); return first + i * stride; } // inverse of element /** * Get the index for this element: inverse of element * @param elem the element of the range * @return index * @throws InvalidRangeException if illegal elem */ public int index(int elem) throws InvalidRangeException { if (elem < first) throw new InvalidRangeException("elem must be >= first"); int result = (elem - first) / stride; if (result > n) throw new InvalidRangeException("elem must be <= first = n * stride"); return result; } /** * Is the ith element contained in this Range? * * @param i index in the original Range * @return true if the ith element would be returned by the Range iterator */ public boolean contains(int i) { if (i < first()) return false; if (i > last()) return false; if (stride == 1) return true; return (i - first) % stride == 0; } /** * Get ith element; skip checking, for speed. * * @param i index of the element * @return the i-th element of a range, no check */ private int elementNC(int i) { return first + i * stride; } /** * @return first in range */ public int first() { return first; } /** * @return last in range, inclusive */ public int last() { return first + (n - 1) * stride; } /** * @return stride, must be >= 1 */ public int stride() { return stride; } /** * Get name * * @return name, or null if none */ public String getName() { return name; } /** * Iterate over Range index * Usage:
   * Iterator iter = range.getIterator();
   * while (iter.hasNext()) {
   *   int index = iter.next();
   *   doSomething(index);
   * }
   * 
* * @return Iterator over element indices */ public Iterator getIterator() { return new Iterator(); } public class Iterator { private int current = 0; public boolean hasNext() { return current < n; } public int next() { return elementNC(current++); } } /** * Find the first element in a strided array after some index start. * Return the smallest element k in the Range, such that
    *
  • k >= first *
  • k >= start *
  • k <= last *
  • k = first + i * stride for some integer i. *
* * @param start starting index * @return first in interval, else -1 if there is no such element. */ public int getFirstInInterval(int start) { if (start > last()) return -1; if (start <= first) return first; if (stride == 1) return start; int offset = start - first; int i = offset/stride; i = (offset % stride == 0) ? i : i+1; // round up return first + i * stride; } public String toString() { return first + ":" + last() + (stride > 1 ? ":" + stride : ""); } /** * Range elements with same first, last, stride are equal. */ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Range)) return false; // this catches nulls Range or = (Range) o; if ((n == 0) && (or.n == 0)) // empty ranges are equal return true; return (or.first == first) && (or.n == n) && (or.stride == stride); } /** * Override Object.hashCode() to implement equals. */ public int hashCode() { if (hashCode == 0) { int result = first(); result = 37 * result + last(); result = 37 * result + stride(); hashCode = result; } return hashCode; } private int hashCode = 0; ////////////////////////////////////////////////////////////////////////// // deprecated /** * @return Minimum index, inclusive. * @deprecated use first() */ public int min() { if (n > 0) { if (stride > 0) return first; else return first + (n - 1) * stride; } else { return first; } } /** * @return Maximum index, inclusive. * @deprecated use last() */ public int max() { if (n > 0) { if (stride > 0) return first + (n - 1) * stride; else return first; } else { if (stride > 0) return first - 1; else return first + 1; } } /////////////////////////////////////////////////////////////////////////// // deprecated - use Section /** * Convert shape array to List of Ranges. Assume 0 origin for all. * * @deprecated use Section(int[] shape) */ public static List factory(int[] shape) { ArrayList result = new ArrayList(); for (int i = 0; i < shape.length; i++) { try { result.add(new Range(0, Math.max(shape[i] - 1, -1))); } catch (InvalidRangeException e) { return null; } } return result; } /** * Check rangeList has no nulls, set from shape array. * * @deprecated use Section.setDefaults(int[] shape) */ public static List setDefaults(List rangeList, int[] shape) { try { // entire rangeList is null if (rangeList == null) { rangeList = new ArrayList(); for (int i = 0; i < shape.length; i++) { rangeList.add(new Range(0, shape[i])); } return rangeList; } // check that any individual range is null for (int i = 0; i < shape.length; i++) { Range r = (Range) rangeList.get(i); if (r == null) { rangeList.set(i, new Range(0, shape[i] - 1)); } } return rangeList; } catch (InvalidRangeException ex) { return null; // could happen if shape[i] is negetive } } /** * Convert shape, origin array to List of Ranges. * * @deprecated use Section(int[] origin, int[] shape) */ public static List factory(int[] origin, int[] shape) throws InvalidRangeException { ArrayList result = new ArrayList(); for (int i = 0; i < shape.length; i++) { try { result.add(new Range(origin[i], origin[i] + shape[i] - 1)); } catch (Exception e) { throw new InvalidRangeException(e.getMessage()); } } return result; } /** * Convert List of Ranges to shape array using the range.length. * * @deprecated use Section.getShape() */ public static int[] getShape(List ranges) { if (ranges == null) return null; int[] result = new int[ranges.size()]; for (int i = 0; i < ranges.size(); i++) { result[i] = ((Range) ranges.get(i)).length(); } return result; } /** * @deprecated use Section.toString() */ public static String toString(List ranges) { if (ranges == null) return ""; StringBuilder sbuff = new StringBuilder(); for (int i = 0; i < ranges.size(); i++) { if (i > 0) sbuff.append(","); sbuff.append(((Range) ranges.get(i)).length()); } return sbuff.toString(); } /** * /** Compute total number of elements represented by the section. * * @param section List of Range objects * @return total number of elements * @deprecated use Section.computeSize() */ static public long computeSize(List section) { int[] shape = getShape(section); return Index.computeSize(shape); } /** * Append a new Range(0,size-1) to the list * * @param ranges list of Range * @param size add this Range * @return same list * @throws InvalidRangeException if size < 1 * @deprecated use Section.appendRange(int size) */ public static List appendShape(List ranges, int size) throws InvalidRangeException { ranges.add(new Range(0, size - 1)); return ranges; } /** * Convert List of Ranges to origin array using the range.first. * * @deprecated use Section.getOrigin() */ public static int[] getOrigin(List ranges) { if (ranges == null) return null; int[] result = new int[ranges.size()]; for (int i = 0; i < ranges.size(); i++) { result[i] = ((Range) ranges.get(i)).first(); } return result; } /** * Convert List of Ranges to array of Ranges. * * * @deprecated use Section.getRanges() */ public static Range[] toArray(List ranges) { if (ranges == null) return null; return (Range[]) ranges.toArray(new Range[ranges.size()]); } /** * Convert array of Ranges to List of Ranges. * * @deprecated use Section.getRanges() */ public static List toList(Range[] ranges) { if (ranges == null) return null; return java.util.Arrays.asList(ranges); } /** * Convert List of Ranges to String Spec. * Inverse of parseSpec * * @deprecated use Section.toString() */ public static String makeSectionSpec(List ranges) { StringBuilder sbuff = new StringBuilder(); for (int i = 0; i < ranges.size(); i++) { Range r = (Range) ranges.get(i); if (i > 0) sbuff.append(","); sbuff.append(r.toString()); } return sbuff.toString(); } /** * Parse an index section String specification, return equivilent list of ucar.ma2.Range objects. * The sectionSpec string uses fortran90 array section syntax, namely: *
   *   sectionSpec := dims
   *   dims := dim | dim, dims
   *   dim := ':' | slice | start ':' end | start ':' end ':' stride
   *   slice := INTEGER
   *   start := INTEGER
   *   stride := INTEGER
   *   end := INTEGER
   * 

* where nonterminals are in lower case, terminals are in upper case, literals are in single quotes. *

* Meaning of index selector : * ':' = all * slice = hold index to that value * start:end = all indices from start to end inclusive * start:end:stride = all indices from start to end inclusive with given stride *

*

* * @param sectionSpec the token to parse, eg "(1:20,:,3,10:20:2)", parenthesis optional * @return return List of ucar.ma2.Range objects corresponding to the index selection. A null * Range means "all" (i.e.":") indices in that dimension. * @throws IllegalArgumentException when sectionSpec is misformed * @deprecated use new Section(String sectionSpec) */ public static List parseSpec(String sectionSpec) throws InvalidRangeException { ArrayList result = new ArrayList(); Range section; StringTokenizer stoke = new StringTokenizer(sectionSpec, "(),"); while (stoke.hasMoreTokens()) { String s = stoke.nextToken().trim(); if (s.equals(":")) section = null; // all else if (s.indexOf(':') < 0) { // just a number : slice try { int index = Integer.parseInt(s); section = new Range(index, index); } catch (NumberFormatException e) { throw new IllegalArgumentException(" illegal selector: " + s + " part of <" + sectionSpec + ">"); } } else { // gotta be "start : end" or "start : end : stride" StringTokenizer stoke2 = new StringTokenizer(s, ":"); String s1 = stoke2.nextToken(); String s2 = stoke2.nextToken(); String s3 = stoke2.hasMoreTokens() ? stoke2.nextToken() : null; try { int index1 = Integer.parseInt(s1); int index2 = Integer.parseInt(s2); int stride = (s3 != null) ? Integer.parseInt(s3) : 1; section = new Range(index1, index2, stride); } catch (NumberFormatException e) { throw new IllegalArgumentException(" illegal selector: " + s + " part of <" + sectionSpec + ">"); } } result.add(section); } return result; } /** * Check ranges are valid * * @param section * @param shape * @return error message, or null if all ok * @deprecated use Section.checkInRange(int shape[]) */ public static String checkInRange(List section, int shape[]) { if (section.size() != shape.length) return "Number of ranges in section must be =" + shape.length; for (int i = 0; i < section.size(); i++) { Range r = (Range) section.get(i); if (r == null) continue; if (r.last() >= shape[i]) return "Illegal range for dimension " + i + ": requested " + r.last() + " >= max " + shape[i]; } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy