/* * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * Copyright 2013 Aurelian Tutuianu * Copyright 2014 Aurelian Tutuianu * Copyright 2015 Aurelian Tutuianu * Copyright 2016 Aurelian Tutuianu * * Licensed 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 rapaio.data; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; /** * Builds a numeric variable which stores values as 32-bit integers. * There are two general usage scenarios: use variable as an * positive integer index or save storage for numeric * variables from Z loosing decimal precision. * <p> * Missing value is {@link Integer#MIN_VALUE}. Any use of this value in * add/set operations will lead to missing values. * <p> * User: Aurelian Tutuianu <padreati@yahoo.com> */ public final class Index extends AbstractVar { /** * Builds an empty index var of size 0 * * @return new instance of index var */ public static Index empty() { return new Index(0, 0, 0); } /** * Builds an index of given size filled with missing values * * @param rows index size * @return new instance of index var */ public static Index empty(int rows) { return new Index(rows, rows, 0); } /** * Builds an index of size 1 filled with the given value * * @param value fill value * @return new instance of index var */ public static Index scalar(int value) { return new Index(1, 1, value); } /** * Builds an index var of given size with given fill value * * @param rows index size * @param value fill value * @return new instance of index var */ public static Index fill(int rows, int value) { return new Index(rows, rows, value); } /** * Builds an index with values copied from a given array * * @param values given array of values * @return new instance of index var */ public static Index copy(int... values) { Index index = new Index(0, 0, 0); index.data = Arrays.copyOf(values, values.length); index.rows = values.length; return index; } /** * Builds an index as a wrapper over a given array of index values * * @param values given array of values * @return new instance of index var */ public static Index wrap(int... values) { Index index = new Index(0, 0, 0); index.data = values; index.rows = values.length; return index; } /** * Builds an index of given size as a ascending sequence starting with 0 * * @param len size of the index * @return new instance of index var */ public static Index seq(int len) { return seq(0, len, 1); } /** * Builds an index of given size as ascending sequence with a given start value * * @param start start value * @param len size of the index * @return new instance of index var */ public static Index seq(int start, int len) { return seq(start, len, 1); } /** * Builds an index of given size as ascending sequence with a given start value and a given step * * @param start start value * @param len size of the index * @param step increment value * @return new instance of index var */ public static Index seq(final int start, final int len, final int step) { Index index = new Index(len, len, 0); int s = start; for (int i = 0; i < len; i++) { index.data[i] = s; s = s + step; } return index; } // private constructor, only public static builders available public static Index from(int len, Function<Integer, Integer> supplier) { Index index = new Index(len, len, 0); for (int i = 0; i < index.data.length; i++) { index.data[i] = supplier.apply(i); } return index; } private static final long serialVersionUID = -2809318697565282310L; private static final int MISSING_VALUE = Integer.MIN_VALUE; private int[] data; private int rows; // static builders private Index(int rows, int capacity, int fill) { if (rows < 0) { throw new IllegalArgumentException("Illegal row count: " + rows); } this.data = new int[capacity]; this.rows = rows; if (fill != 0) Arrays.fill(data, 0, rows, fill); } public static Collector<? super Integer, Index, Index> collector() { return new Collector<Integer, Index, Index>() { @Override public Supplier<Index> supplier() { return Index::empty; } @Override public BiConsumer<Index, Integer> accumulator() { return Index::addIndex; } @Override public BinaryOperator<Index> combiner() { return (x, y) -> { y.stream().forEach(s -> x.addValue(s.value())); return x; }; } @Override public Function<Index, Index> finisher() { return Index::solidCopy; } @Override public Set<Characteristics> characteristics() { return new HashSet<>(); } }; } @Override public Index withName(String name) { return (Index) super.withName(name); } private void ensureCapacityInternal(int minCapacity) { // overflow-conscious code if (minCapacity < data.length) return; int oldCapacity = data.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // minCapacity is usually close to size, so this is a win: data = Arrays.copyOf(data, newCapacity); } @Override public VarType type() { return VarType.INDEX; } @Override public int rowCount() { return rows; } @Override public void addRows(int rowCount) { ensureCapacityInternal(this.rows + rowCount + 1); for (int i = 0; i < rowCount; i++) { data[rows + i] = Index.MISSING_VALUE; } rows += rowCount; } @Override public int index(int row) { return data[row]; } @Override public void setIndex(int row, int value) { data[row] = value; } @Override public void addIndex(int value) { ensureCapacityInternal(rows + 1); data[rows] = value; rows++; } @Override public double value(int row) { return index(row); } @Override public void setValue(int row, double value) { setIndex(row, (int) Math.rint(value)); } @Override public void addValue(double value) { addIndex((int) Math.rint(value)); } @Override public String label(int row) { if (missing(row)) return "?"; return String.valueOf(index(row)); } @Override public void setLabel(int row, String value) { if ("?".equals(value)) { setMissing(row); return; } setIndex(row, Integer.parseInt(value)); } @Override public void addLabel(String value) { if ("?".equals(value)) { addMissing(); return; } addIndex(Integer.parseInt(value)); } @Override public String[] levels() { throw new IllegalArgumentException("Operation not available for index vectors."); } @Override public void setLevels(String[] dict) { throw new IllegalArgumentException("Operation not available for index vectors."); } @Override public boolean binary(int row) { return index(row) == 1; } @Override public void setBinary(int row, boolean value) { setIndex(row, value ? 1 : 0); } @Override public void addBinary(boolean value) { addIndex(value ? 1 : 0); } @Override public long stamp(int row) { return index(row); } @Override public void setStamp(int row, long value) { setIndex(row, Integer.valueOf(String.valueOf(value))); } @Override public void addStamp(long value) { addIndex(Integer.valueOf(String.valueOf(value))); } @Override public boolean missing(int row) { return index(row) == MISSING_VALUE; } @Override public void setMissing(int row) { setIndex(row, MISSING_VALUE); } @Override public void addMissing() { addIndex(MISSING_VALUE); } @Override public void remove(int index) { if (index > rows || index < 0) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + rows); int numMoved = rows - index - 1; if (numMoved > 0) { System.arraycopy(data, index + 1, data, index, numMoved); rows--; } } @Override public void clear() { rows = 0; } @Override public Var newInstance(int rows) { return Index.empty(rows); } @Override public String toString() { return "Index[name:" + name() + ", rowCount:" + rowCount() + "]"; } @Override public Index solidCopy() { return (Index) super.solidCopy(); } private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(rowCount()); for (int i = 0; i < rowCount(); i++) { out.writeInt(data[i]); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { rows = in.readInt(); data = new int[rows]; for (int i = 0; i < rows; i++) { data[i] = in.readInt(); } } }