| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.math; |
| |
| import java.io.Serializable; |
| import java.util.Objects; |
| import java.util.function.IntSupplier; |
| import org.apache.sis.measure.NumberRange; |
| import org.apache.sis.util.resources.Errors; |
| |
| |
| /** |
| * A vector whose values are the repetitions of the values given in a base vector. |
| * This vector can be created as a result of {@link Vector#compress(double)}. |
| * The intent is to handle the cases found in netCDF files where localization grids |
| * (e.g. the {@code "longitude"} variable storing longitude values of all points in a grid) |
| * contains a lot of repetitions. |
| * |
| * <p>{@link #cycleLength} is usually the length of the {@linkplain #base} vector, but not necessarily. |
| * If {@link #occurrences} = 1 and {@code cycleLength} = 4 for example, then this class handles repetitions like below:</p> |
| * |
| * <pre class="text"> |
| * 10 12 15 20 |
| * 10 12 15 20 ← new cycle |
| * 10 12 15 20 ← new cycle |
| * 10 12 15 20 ← new cycle |
| * …etc…</pre> |
| * |
| * If {@link #occurrences} > 1, then this class handles repetitions in a different way |
| * (in this example, {@link #cycleLength} is still 4): |
| * |
| * <pre class="text"> |
| * 10 10 10 10 |
| * 12 12 12 12 |
| * 15 15 15 15 |
| * 20 20 20 20 |
| * 10 10 10 10 ← new cycle |
| * 12 12 12 12 |
| * …etc…</pre> |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| */ |
| final class RepeatedVector extends Vector implements Serializable { |
| /** |
| * For cross-version compatibility. |
| */ |
| private static final long serialVersionUID = 3607036775685492552L; |
| |
| /** |
| * The vector on which this vector is derived from. |
| */ |
| @SuppressWarnings("serial") // Most SIS implementations are serializable. |
| private final Vector base; |
| |
| /** |
| * Number of times that each {@linkplain #base} element appears in a row before to move |
| * to the next {@code base} element. See class javadoc for more information. |
| */ |
| private final int occurrences; |
| |
| /** |
| * Length of the sequence of values to repeat, after conversion to base vector indices. |
| * Usually equals to the length of the {@linkplain #base} vector but can also be smaller. |
| * Shall not be greater than {@code base.size()}. See class javadoc for more information. |
| */ |
| private final int cycleLength; |
| |
| /** |
| * The size of this vector. |
| * This is often {@link #cycleLength} × {@link #occurrences}, but not necessarily. |
| */ |
| private final int size; |
| |
| /** |
| * Creates a new vector of repeated data. |
| * |
| * @param base the vector on which this vector is derived from. |
| * @param occurrences number of time that each element is repeated. |
| * @param cycleLength length of the sequence of values to repeat. |
| * @param size this vector size, usually {@code base.size() * repetition}. |
| */ |
| RepeatedVector(final Vector base, final int occurrences, final int cycleLength, final int size) { |
| this.base = base; |
| this.occurrences = occurrences; |
| this.cycleLength = cycleLength; |
| this.size = size; |
| assert cycleLength <= base.size() : cycleLength; |
| } |
| |
| /** |
| * Creates a vector of repeated data from the result of a call to {@link Vector#repetitions(int...)}. |
| * |
| * @param base the vector on which this vector is derived from. |
| * @param repetitions results of {@link Vector#repetitions(int...)}. Must be non-empty. |
| * @param tolerance tolerance factor for compression of the base vector. |
| */ |
| RepeatedVector(final Vector base, final int[] repetitions, final double tolerance) { |
| size = base.size(); |
| occurrences = repetitions[0]; |
| cycleLength = (repetitions.length >= 2) ? repetitions[1] : size / occurrences; |
| this.base = base.subSampling(0, occurrences, cycleLength).compress(tolerance); |
| } |
| |
| /** |
| * Converts the given index from this vector domain to an index in the {@linkplain #base} vector. |
| */ |
| private int toBase(final int index) { |
| return (Objects.checkIndex(index, size) / occurrences) % cycleLength; |
| } |
| |
| /** |
| * Returns the type of values, which is inherited from the {@linkplain #base} vector. |
| */ |
| @Override |
| public final Class<? extends Number> getElementType() { |
| return base.getElementType(); |
| } |
| |
| /** Forwards to the base vector. */ |
| @Override public final boolean isEmptyOrNaN() {return base.isEmptyOrNaN();} |
| @Override public final boolean isSinglePrecision() {return base.isSinglePrecision();} |
| @Override public final boolean isInteger() {return base.isInteger();} |
| @Override public final boolean isUnsigned() {return base.isUnsigned();} |
| @Override public final int size() {return size;} |
| @Override public final boolean isNaN (int i) {return base.isNaN (toBase(i));} |
| @Override public final double doubleValue(int i) {return base.doubleValue(toBase(i));} |
| @Override public final float floatValue (int i) {return base.floatValue (toBase(i));} |
| @Override public final long longValue (int i) {return base.longValue (toBase(i));} |
| @Override public final int intValue (int i) {return base.intValue (toBase(i));} |
| @Override public final short shortValue (int i) {return base.shortValue (toBase(i));} |
| @Override public final byte byteValue (int i) {return base.byteValue (toBase(i));} |
| @Override public final String stringValue(int i) {return base.stringValue(toBase(i));} |
| @Override public final Number get (int i) {return base.get (toBase(i));} |
| |
| /** |
| * The range of values in this vector is the range of values in the {@linkplain #base} vector |
| * if we use all its data. |
| */ |
| @Override |
| public final NumberRange<?> range() { |
| return (cycleLength == base.size()) ? base.range() : super.range(); |
| } |
| |
| /** |
| * Overridden for efficiency in case {@link #base} itself overrides that method. |
| * Overriding that method is optional; the default implementation would have worked. |
| */ |
| @Override |
| final NumberRange<?> range(final IntSupplier indices, final int count) { |
| return base.range(() -> toBase(indices.getAsInt()), count); |
| } |
| |
| /** |
| * Do not allow setting values. |
| */ |
| @Override |
| public final Number set(int index, Number value) { |
| throw new UnsupportedOperationException(Errors.format(Errors.Keys.CanNotStoreInVector_1, value)); |
| } |
| |
| /** |
| * Returns the parameters used by this {@code RepeatedVector} instance on the assumption |
| * that they are the result of a previous invocation to {@link Vector#repetitions(int...)}. |
| */ |
| @Override |
| public int[] repetitions(final int... candidates) { |
| if (cycleLength * occurrences >= size) { |
| return new int[] {occurrences}; |
| } else { |
| return new int[] {occurrences, cycleLength}; |
| } |
| } |
| |
| /** |
| * Returns a vector whose value is the content of this vector repeated <var>count</var> times. |
| */ |
| @Override |
| @SuppressWarnings("ReturnOfCollectionOrArrayField") |
| public Vector repeat(final boolean eachValue, final int count) { |
| opti: if (count > 1 && cycleLength * occurrences >= size) { |
| final int n; |
| if (eachValue) { |
| if (cycleLength < base.size()) break opti; |
| n = occurrences; |
| } else { |
| if (occurrences != 1) break opti; |
| n = cycleLength; |
| } |
| return base.repeat(eachValue, Math.multiplyExact(n, count)); |
| } |
| return super.repeat(eachValue, count); |
| } |
| |
| /** |
| * Returns {@code null} since the repetition of a sequence of numbers implies that there is no regular increment. |
| * An exception to this rule would be if the {@linkplain #base} vector contains a constant value or if the repetition |
| * is exactly 1, but we should not have created a {@code RepeatedVector} in such cases. |
| */ |
| @Override |
| public final Number increment(final double tolerance) { |
| return null; |
| } |
| |
| /** |
| * Returns {@code this} since this vector is considered already compressed. Actually it may be possible to compress more |
| * if the {@linkplain #base} vector has been modified after {@code RepeatedVector} construction. But it should not happen |
| * since this vector is read-only and {@link Vector#compress(double)} recommends to not keep reference to the original vector. |
| */ |
| @Override |
| @SuppressWarnings("ReturnOfCollectionOrArrayField") |
| public Vector compress(final double tolerance) { |
| return this; |
| } |
| |
| /** |
| * Informs {@link #pick(int...)} that this vector is backed by another vector. |
| */ |
| @Override |
| @SuppressWarnings("ReturnOfCollectionOrArrayField") |
| final Vector backingVector() { |
| return base; |
| } |
| |
| /** |
| * Converts an array of indexes used by this vector to the indexes used by the backing vector. |
| * This method must also check index validity. |
| */ |
| @Override |
| final int[] toBacking(int[] indices) { |
| indices = indices.clone(); |
| for (int i=0; i<indices.length; i++) { |
| indices[i] = toBase(indices[i]); |
| } |
| return indices; |
| } |
| |
| /** |
| * Implementation of {@link #subSampling(int,int,int)}. |
| * Arguments validity has been verified by the caller. |
| * |
| * @param first index of the first value to be included in the returned view. |
| * @param step the index increment in this vector between two consecutive values |
| * in the returned vector. Can be positive, zero or negative. |
| * @param length the length of the vector to be returned. Cannot be greater than |
| * the length of this vector, except if the {@code step} is zero. |
| */ |
| @Override |
| @SuppressWarnings("ReturnOfCollectionOrArrayField") |
| Vector createSubSampling(final int first, final int step, final int length) { |
| /* |
| * If the sub-range is such that there is no more repetition, |
| * return the base vector (or a sub-range of it) directly. |
| */ |
| if ((step % occurrences) == 0) { |
| final int bs = step / occurrences; // Step in the base vector. |
| final int lower = first / occurrences; // First index in the base vector (inclusive). |
| final int upper = lower + (length-1) * bs; // Last index in the base vector (inclusive). |
| if (lower >= 0 && lower <= upper && upper < base.size() |
| && (lower / cycleLength) == (upper / cycleLength)) // Lower et upper must be member of the same cycle. |
| { |
| return base.subSampling(lower, bs, length); |
| } |
| } |
| /* |
| * We still have repetitions. Return another RepeatedVector if possible. |
| * Fallback on SubSampling wrapper only in last resort. |
| */ |
| if (first < occurrences && (occurrences % step) == 0) { |
| return new RepeatedVector(base, occurrences / step, cycleLength, length); |
| } |
| return super.createSubSampling(first, step, length); |
| } |
| } |