/*
* 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.util.Arrays;
import java.util.function.Supplier;
/**
* Variable which stores long 64-bit integer values.
* Basically the algorithms uses double for computations, so any
* usage of stamps would fail to work. However, for some certain
* necessary and specific usage scenarios this type of variable
* is useful. One plausible scenario is the representation on
* time stamps.
* <p>
* Created by <a href="mailto:padreati@yahoo.com">Aurelian Tutuianu</a>
*/
public class Stamp extends AbstractVar {
/**
* Builds an empty stamp var of size 0
*
* @return new instance of stamp var
*/
public static Stamp empty() {
return new Stamp(0, 0, MISSING_VALUE);
}
/**
* Builds a stamp var of given size with missing values
*
* @param rows var size
* @return new instance of stamp var
*/
public static Stamp empty(int rows) {
return new Stamp(rows, rows, MISSING_VALUE);
}
/**
* Builds a stamp var of size 1 with given fill value
*
* @param value fill value
* @return new instance of stamp var
*/
public static Stamp scalar(long value) {
return new Stamp(1, 1, value);
}
/**
* Builds a stamp var of given size with given fill value
*
* @param rows var size
* @param value fill value
* @return new instance of stamp var
*/
public static Stamp fill(int rows, long value) {
return new Stamp(rows, rows, value);
}
/**
* Builds a stamp var with values copied from given array
*
* @param values array of value
* @return new instance of stamp var
*/
public static Stamp copy(long... values) {
Stamp stamp = new Stamp(0, 0, 0);
stamp.data = Arrays.copyOf(values, values.length);
stamp.rows = values.length;
return stamp;
}
/**
* Builds a stamp var as a wrapper over the arrat of long values
*
* @param values wrapped array of values
* @return new instance of stamp var
*/
public static Stamp wrap(long... values) {
Stamp stamp = new Stamp(0, 0, 0);
stamp.data = values;
stamp.rows = values.length;
return stamp;
}
/**
* Builds a stamp var with values as a sequence in increasing order starting with 0 and of given length
*
* @param len size of the var
* @return new instance of stamp var
*/
public static Stamp seq(int len) {
return seq(0, len, 1);
}
/**
* Builds a stamp var with values as an increasing sequence starting with a given start point and of given length
*
* @param start start value
* @param len size of the var
* @return new instance of stamp var
*/
public static Stamp seq(long start, int len) {
return seq(start, len, 1);
}
/**
* Builds a stamp var with values as an increasing sequence of values with a given start,
* of a given length and with a given step increment value
*
* @param start start of the sequence
* @param len size of the sequence
* @param step step/increment value
* @return new instance of stamp var
*/
public static Stamp seq(final long start, final int len, final long step) {
Stamp stamp = new Stamp(len, len, 0);
long s = start;
for (int i = 0; i < len; i++) {
stamp.data[i] = s;
s = s + step;
}
return stamp;
}
public static final long MISSING_VALUE = Long.MIN_VALUE;
private static final long serialVersionUID = -6387573611986137666L;
private long[] data;
private int rows;
// static builders
private Stamp(int rows, int capacity, long fill) {
super();
if (rows < 0) {
throw new IllegalArgumentException("Illegal row count: " + rows);
}
this.data = new long[capacity];
this.rows = rows;
if (fill != 0)
Arrays.fill(data, 0, rows, fill);
}
// private constructor, only public static builders available
public static Stamp from(int rows, Supplier<Long> supplier) {
Stamp var = Stamp.empty();
for (int i = 0; i < rows; i++) {
var.addStamp(supplier.get());
}
return var;
}
@Override
public Stamp withName(String name) {
return (Stamp) super.withName(name);
}
private void ensureCapacityInternal(int minCapacity) {
if (minCapacity <= data.length)
return;
// overflow-conscious code
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.STAMP;
}
private void rangeCheck(int index) {
if (index > rows || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Stamp: " + index + ", Size: " + rows;
}
@Override
public int rowCount() {
return rows;
}
@Override
public Var bindRows(Var var) {
return BoundVar.from(this, var);
}
@Override
public Var mapRows(Mapping mapping) {
return MappedVar.byRows(this, mapping);
}
@Override
public void addRows(int rowCount) {
ensureCapacityInternal(this.rows + rowCount + 1);
for (int i = 0; i < rowCount; i++) {
data[rows + i] = Stamp.MISSING_VALUE;
}
rows += rowCount;
}
@Override
public int index(int row) {
return (int) stamp(row);
}
@Override
public void setIndex(int row, int value) {
setStamp(row, value);
}
@Override
public void addIndex(int value) {
addStamp(value);
}
@Override
public double value(int row) {
return stamp(row);
}
@Override
public void setValue(int row, double value) {
setStamp(row, (long) Math.rint(value));
}
@Override
public void addValue(double value) {
addStamp((long) Math.rint(value));
}
@Override
public String label(int row) {
return String.valueOf(stamp(row));
}
@Override
public void setLabel(int row, String value) {
setStamp(row, Long.parseLong(value));
}
@Override
public void addLabel(String value) {
addStamp(Long.parseLong(value));
}
@Override
public String[] levels() {
throw new IllegalArgumentException("Operation not available for stamp variable");
}
@Override
public void setLevels(String[] dict) {
throw new IllegalArgumentException("Operation not available for stamp variable");
}
@Override
public boolean binary(int row) {
if (stamp(row) == 1) return true;
if (stamp(row) == 0) return false;
throw new IllegalArgumentException("Stamp value could not be represented as binary value");
}
@Override
public void setBinary(int row, boolean value) {
setStamp(row, value ? 1 : 0);
}
@Override
public void addBinary(boolean value) {
addStamp(value ? 1 : 0);
}
@Override
public long stamp(int row) {
return data[row];
}
@Override
public void setStamp(int row, long value) {
data[row] = value;
}
@Override
public void addStamp(long value) {
ensureCapacityInternal(rows + 1);
data[rows] = value;
rows++;
}
@Override
public boolean missing(int row) {
return stamp(row) == MISSING_VALUE;
}
@Override
public void setMissing(int row) {
setStamp(row, MISSING_VALUE);
}
@Override
public void addMissing() {
addStamp(MISSING_VALUE);
}
@Override
public void remove(int row) {
rangeCheck(row);
int numMoved = rows - row - 1;
if (numMoved > 0) {
System.arraycopy(data, row + 1, data, row, numMoved);
rows--;
}
}
@Override
public void clear() {
rows = 0;
}
@Override
public Var newInstance(int rows) {
return Stamp.empty(rows);
}
@Override
public Stamp solidCopy() {
return (Stamp) super.solidCopy();
}
@Override
public String toString() {
return "Stamp[" + rowCount() + "]";
}
}