/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.example.table.internal;
import java.io.Serializable;
import java.util.Arrays;
import com.rapidminer.example.utils.ExampleSetBuilder.DataManagement;
/**
* {@link Column} that stores double values in chunks. The chunks can either be sparse or dense and
* switch automatically to the appropriate format for the given values. This column assumes that
* {@link complete} is never called to indicate that the column is finished.
*
* @author Gisa Schaefer
* @since 7.3.1
*/
final class DoubleIncompleteAutoColumn implements Column {
private static final long serialVersionUID = 1L;
/**
* Building block of a {@link DoubleIncompleteAutoColumn}.
*
* @author Gisa Schaefer
*
*/
static abstract class DoubleIncompleteAutoChunk implements Serializable {
private static final long serialVersionUID = 1L;
/**
* the position of this chunk in {@link DoubleIncompleteAutoColumn#chunks}
*/
final int id;
/**
* the chunk array {@link DoubleIncompleteAutoColumn#chunks}
*/
final DoubleIncompleteAutoChunk[] chunks;
/**
* decides about sparsity thresholds
*/
final DataManagement management;
DoubleIncompleteAutoChunk(int id, DoubleIncompleteAutoChunk[] chunks, DataManagement management) {
this.id = id;
this.chunks = chunks;
this.management = management;
}
/**
* Ensures that the internal data structure can hold up to {@code size} values.
*
* @param size
* the size that should be ensured
*/
abstract void ensure(int size);
/**
* Gets the value at the specified row.
*
* @param row
* the row that should be looked up
* @return the value at the specified row
*/
abstract double get(int row);
/**
* Sets the value at the specified row to the given value.
*
* @param row
* the row that should be set
* @param value
* the value that should be set at the row
*/
abstract void set(int row, double value);
}
private DoubleIncompleteAutoChunk[] chunks = new DoubleIncompleteAutoChunk[1];
private int chunkCount = 0;
private int ensuredSize = 0;
private final DataManagement management;
/**
* Constructs a column with enough chunks to fit size values.
*
* @param size
* the size of the column
*/
DoubleIncompleteAutoColumn(int size, DataManagement management) {
this.management = management;
ensure(size);
}
@Override
public double get(int row) {
return chunks[row >> AutoColumnUtils.CHUNK_SIZE_EXP].get(row & AutoColumnUtils.CHUNK_MODULO_MASK);
}
@Override
public void set(int row, double value) {
chunks[row >> AutoColumnUtils.CHUNK_SIZE_EXP].set(row & AutoColumnUtils.CHUNK_MODULO_MASK, value);
}
@Override
public void setLast(int row, double value) {
set(row, value);
}
@Override
public void ensure(int size) {
ensureChunks(size);
int completeChunks = 0;
boolean enlargeLastChunk = false;
if (chunkCount > 0) {
if (ensuredSize % AutoColumnUtils.CHUNK_SIZE > 0) {
completeChunks = chunkCount - 1;
enlargeLastChunk = true;
} else {
completeChunks = chunkCount;
}
}
int rowsLeft = size - completeChunks * AutoColumnUtils.CHUNK_SIZE;
while (rowsLeft > 0) {
int chunkSize = Math.min(rowsLeft, AutoColumnUtils.CHUNK_SIZE);
if (enlargeLastChunk) {
chunks[chunkCount - 1].ensure(chunkSize);
enlargeLastChunk = false;
} else {
if (management == DataManagement.MEMORY_OPTIMIZED) {
// create sparse chunk with guessed default value 0
DoubleIncompleteSparseChunk sparse = new DoubleIncompleteSparseChunk(chunkCount, chunks, 0, management);
sparse.hasGuessedDefault();
sparse.ensure(chunkSize);
chunks[chunkCount] = sparse;
} else {
chunks[chunkCount] = new DoubleIncompleteDenseChunk(chunkCount, chunks, chunkSize, management);
}
chunkCount++;
}
rowsLeft -= chunkSize;
}
ensuredSize = size;
}
/**
* Ensures that the chunks array is big enough to hold all chunks.
*
* @param numberOfRows
* the number of rows that should be stored in the chunks
*/
private void ensureChunks(int numberOfRows) {
int chunksNeeded = numberOfRows / AutoColumnUtils.CHUNK_SIZE + 1;
if (chunksNeeded <= chunks.length) {
return;
}
int chunksMinGrowth = chunks.length == 1 ? 2 : chunks.length + (chunks.length >> 1);
int newLength = Math.min(AutoColumnUtils.MAXIMAL_CHUNKS, Math.max(chunksNeeded, chunksMinGrowth));
chunks = Arrays.copyOf(chunks, newLength);
}
}