/*
 * Copyright (C) 2008-2015 by Holger Arndt
 *
 * This file is part of the Universal Java Matrix Package (UJMP).
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * UJMP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * UJMP 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with UJMP; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package org.ujmp.core.doublematrix.impl;

import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.ujmp.core.Coordinates;
import org.ujmp.core.Matrix;
import org.ujmp.core.doublematrix.stub.AbstractDenseDoubleMatrixMultiD;
import org.ujmp.core.interfaces.Erasable;
import org.ujmp.core.mapmatrix.MapMatrix;
import org.ujmp.core.util.MathUtil;
import org.ujmp.core.util.io.BufferedRandomAccessFile;

public class DenseFileMatrix extends AbstractDenseDoubleMatrixMultiD implements Erasable, Closeable {
	private static final long serialVersionUID = 1754729146021609978L;

	private transient BufferedRandomAccessFile randomAccessFile = null;

	private int bufferSize = 65536;

	public static final int BYTE = 0;
	public static final int CHAR = 1;
	public static final int DOUBLE = 2;
	public static final int FLOAT = 3;
	public static final int INT = 4;
	public static final int LONG = 5;
	public static final int SHORT = 6;
	public static final int UNSIGNEDBYTE = 7;
	public static final int UNSIGNEDSHORT = 8;
	public static final int SHORTLITTLEENDIAN = 9;
	public static final int INTLITTLEENDIAN = 10;
	public static final int LONGLITTLEENDIAN = 11;
	public static final int BOOLEAN = 12;

	private int dataType = DOUBLE;

	private File file = null;

	private long offset = 0;

	private int bitsPerValue = 1;

	private boolean readOnly = false;

	private static ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;

	public DenseFileMatrix(File file, long... size) throws IOException {
		this(file, 0, DOUBLE, false, size);
	}

	public DenseFileMatrix(File file) throws IOException {
		this(file, 0, BYTE, true, file.length(), 1);
	}

	public DenseFileMatrix(File file, int dataType, long... size) throws IOException {
		this(file, 0, dataType, false, size);
	}

	public DenseFileMatrix(File file, long offset, int dataType, boolean readOnly, long... size)
			throws IOException {
		this(65536, file, offset, dataType, readOnly, size);
	}

	public DenseFileMatrix(int bufferSize, File file, long offset, int dataType, boolean readOnly,
			long... size) throws IOException {
		super(size);
		if (file == null) {
			file = File.createTempFile("denseFileMatrix", ".dat");
			file.deleteOnExit();
		}
		this.bufferSize = bufferSize;
		this.file = file;
		this.size = size;
		this.offset = offset;
		this.dataType = dataType;
		this.bitsPerValue = getBitsPerValue(dataType);
		this.readOnly = readOnly;
	}

	public int getBufferSize() {
		return bufferSize;
	}

	public void setBufferSize(int bufferSize) {
		this.bufferSize = bufferSize;
	}

	private void createFile() {
		try {
			if (readOnly) {
				randomAccessFile = new BufferedRandomAccessFile(file, "r", bufferSize);
			} else {
				try {
					randomAccessFile = new BufferedRandomAccessFile(file, "rw", bufferSize);
				} catch (FileNotFoundException e) {
					randomAccessFile = new BufferedRandomAccessFile(file, "r", bufferSize);
				}
				long difference = getFileLength() + offset - randomAccessFile.length();
				if (difference > 0) {
					long seek = (long) (Math.ceil(getPos(getRowCount(), getColumnCount())));
					switch (dataType) {
					case BYTE:
						randomAccessFile.writeByte(seek, (byte) 0.0);
						break;
					case CHAR:
						randomAccessFile.writeChar(seek, (char) 0.0);
						break;
					case DOUBLE:
						randomAccessFile.writeDouble(seek, 0.0);
						break;
					case FLOAT:
						randomAccessFile.writeFloat(seek, (float) 0.0);
						break;
					case SHORT:
						randomAccessFile.writeShort(seek, (short) 0.0);
						break;
					case INT:
						randomAccessFile.writeInt(seek, (int) 0.0);
						break;
					case LONG:
						randomAccessFile.writeLong(seek, (long) 0.0);
						break;
					case UNSIGNEDBYTE:
						randomAccessFile.writeByte(seek, (byte) 0.0);
						break;
					case UNSIGNEDSHORT:
						randomAccessFile.writeShort(seek, (short) 0.0);
						break;
					case INTLITTLEENDIAN:
						randomAccessFile.writeInt(seek, (int) 0.0);
						break;
					case SHORTLITTLEENDIAN:
						randomAccessFile.writeInt(seek, (short) 0.0);
						break;
					case LONGLITTLEENDIAN:
						randomAccessFile.writeLong(seek, (long) 0.0);
						break;
					case BOOLEAN:
						randomAccessFile.writeByte(seek, (byte) 0.0);
						break;
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("could not open file", e);
		}
	}

	public DenseFileMatrix(long... size) throws IOException {
		this(null, size);
	}

	public DenseFileMatrix(Matrix m) throws IOException {
		this(m.getSize());
		for (long[] c : m.allCoordinates()) {
			setAsDouble(m.getAsDouble(c), c);
		}
		MapMatrix a = m.getMetaData();
		if (a != null) {
			setMetaData(a.clone());
		}
	}

	public BufferedRandomAccessFile getRandomAccessFile() {
		return randomAccessFile;
	}

	public File getFile() {
		return file;
	}

	private static final int getBitsPerValue(int dataType) {
		switch (dataType) {
		case BYTE:
			return 8;
		case CHAR:
			return 8;
		case DOUBLE:
			return 64;
		case FLOAT:
			return 32;
		case INT:
			return 32;
		case INTLITTLEENDIAN:
			return 32;
		case LONG:
			return 64;
		case LONGLITTLEENDIAN:
			return 64;
		case SHORT:
			return 16;
		case UNSIGNEDBYTE:
			return 8;
		case UNSIGNEDSHORT:
			return 16;
		case SHORTLITTLEENDIAN:
			return 16;
		case BOOLEAN:
			return 1;
		default:
			return 32;
		}
	}

	public long getBytesPerValue() {
		return getBitsPerValue() / 8;
	}

	public int getBitsPerValue() {
		return bitsPerValue;
	}

	private long getPos(long... pos) {
		if (getBytesPerValue() < 1) {
			throw new RuntimeException("not supported");
		}
		return MathUtil.pos2IndexRowMajor(size, pos) * getBytesPerValue() + offset;
	}

	public long getFileLength() {
		double prod = (double) getRowCount() * (double) getColumnCount() * getBytesPerValue();
		return (long) Math.ceil(prod);
	}

	public int getDataType() {
		return dataType;
	}

	public synchronized double getDouble(long... c) {
		if (randomAccessFile == null) {
			createFile();
		}
		if (randomAccessFile != null) {
			try {
				long seek = getPos(c);

				if (seek < getFileLength() + offset) {

					byte[] bytes = null;

					switch (getDataType()) {
					case BYTE:
						bytes = new byte[1];
						randomAccessFile.read(seek, bytes);
						return bytes[0];
					case CHAR:
						bytes = new byte[1];
						randomAccessFile.read(seek, bytes);
						return bytes[0];
					case DOUBLE:
						bytes = new byte[8];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getDouble();
					case FLOAT:
						bytes = new byte[4];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getFloat();
					case SHORT:
						bytes = new byte[2];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getShort();
					case INT:
						bytes = new byte[4];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getInt();
					case LONG:
						bytes = new byte[8];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getLong();
					case LONGLITTLEENDIAN:
						bytes = new byte[8];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getLong();
					case SHORTLITTLEENDIAN:
						bytes = new byte[2];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getShort();
					case INTLITTLEENDIAN:
						bytes = new byte[4];
						randomAccessFile.read(seek, bytes);
						return ByteBuffer.wrap(bytes).order(byteOrder).getInt();
					case UNSIGNEDBYTE:
						return randomAccessFile.readUnsignedByte();
					case UNSIGNEDSHORT:
						return randomAccessFile.readUnsignedShort();
					case BOOLEAN:
						// return getBit(randomAccessFile.readByte(), pos -
						// Math.floor(pos));
					}

				} else {
					throw new RuntimeException("no such coordinates: " + Coordinates.toString(c));
				}
			} catch (Exception e) {
				throw new RuntimeException("could not read value", e);
			}
		}
		return 0.0;
	}

	public void setSize(long... size) {
		this.size = size;
	}

	private static final double getBit(byte b, double offset) {
		if (offset == 0.0) {
			return ((b & 0x01) > 0) ? 1 : 0;
		} else if (offset == 0.125) {
			return ((b & 0x02) > 0) ? 1 : 0;
		} else if (offset == 0.25) {
			return ((b & 0x04) > 0) ? 1 : 0;
		} else if (offset == 0.375) {
			return ((b & 0x08) > 0) ? 1 : 0;
		} else if (offset == 0.5) {
			return ((b & 0x10) > 0) ? 1 : 0;
		} else if (offset == 0.625) {
			return ((b & 0x20) > 0) ? 1 : 0;
		} else if (offset == 0.75) {
			return ((b & 0x40) > 0) ? 1 : 0;
		} else if (offset == 0.875) {
			return ((b & 0x80) > 0) ? 1 : 0;
		}
		return 0.0;
	}

	private static final byte setBit(byte b, double offset) {
		if (offset == 0.0) {
			b = (byte) (b | 0x01);
		} else if (offset == 0.125) {
			b = (byte) (b | 0x02);
		} else if (offset == 0.25) {
			b = (byte) (b | 0x04);
		} else if (offset == 0.375) {
			b = (byte) (b | 0x08);
		} else if (offset == 0.5) {
			b = (byte) (b | 0x10);
		} else if (offset == 0.625) {
			b = (byte) (b | 0x20);
		} else if (offset == 0.75) {
			b = (byte) (b | 0x40);
		} else if (offset == 0.875) {
			b = (byte) (b | 0x80);
		}
		return b;
	}

	public synchronized void setDouble(double value, long... c) {
		if (isReadOnly())
			return;

		try {

			if (file == null) {
				file = File.createTempFile("matrix", null);
			}

			if (randomAccessFile == null) {
				createFile();
			}

			long seek = getPos(c);

			ByteBuffer bb = null;

			switch (dataType) {
			case BYTE:
				randomAccessFile.writeByte(seek, (byte) value);
				break;
			case CHAR:
				randomAccessFile.writeChar(seek, (char) value);
				break;
			case DOUBLE:
				randomAccessFile.writeDouble(seek, value);
				break;
			case FLOAT:
				randomAccessFile.writeFloat(seek, (float) value);
				break;
			case SHORT:
				randomAccessFile.writeShort(seek, (short) value);
				break;
			case SHORTLITTLEENDIAN:
				bb = ByteBuffer.allocate(2).order(byteOrder);
				bb.putShort((short) value);
				randomAccessFile.write(seek, bb.array());
				break;
			case INT:
				randomAccessFile.writeInt(seek, (int) value);
				break;
			case INTLITTLEENDIAN:
				bb = ByteBuffer.allocate(4).order(byteOrder);
				bb.putInt((int) value);
				randomAccessFile.write(seek, bb.array());
				break;
			case LONG:
				randomAccessFile.writeLong(seek, (long) value);
				break;
			case LONGLITTLEENDIAN:
				bb = ByteBuffer.allocate(8).order(byteOrder);
				bb.putLong((long) value);
				randomAccessFile.write(seek, bb.array());
				break;
			case UNSIGNEDBYTE:
				randomAccessFile.writeByte(seek, (byte) value);
				break;
			case UNSIGNEDSHORT:
				randomAccessFile.writeShort(seek, (short) value);
				break;
			case BOOLEAN:
				throw new IOException("not supported");
			}
		} catch (Exception e) {
			throw new RuntimeException("could not write value at coordinates "
					+ Coordinates.toString(c), e);
		}
	}

	protected void finalize() throws Throwable {
		super.finalize();
		if (randomAccessFile != null) {
			try {
				randomAccessFile.close();
				randomAccessFile = null;
			} catch (Throwable e) {
			}
		}
	}

	public boolean isReadOnly() {
		return readOnly;
	}

	public static final int getShortLittleEndian(byte[] bytes) {
		return ByteBuffer.wrap(bytes).order(byteOrder).getShort();
	}

	public static final int getIntLittleEndian(byte[] bytes) {
		return ByteBuffer.wrap(bytes).order(byteOrder).getInt();
	}

	private void writeObject(ObjectOutputStream s) throws IOException {
		s.defaultWriteObject();
		for (long[] c : availableCoordinates()) {
			s.writeObject(Coordinates.wrap(c).clone());
			s.writeObject(getDouble(c));
		}
	}

	private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
		s.defaultReadObject();
		while (true) {
			try {
				Coordinates c = (Coordinates) s.readObject();
				Double o = (Double) s.readObject();
				setDouble(o, c.getLongCoordinates());
			} catch (OptionalDataException e) {
				return;
			}
		}
	}

	public void erase() throws IOException {
		close();
		file.delete();
	}

	public void close() throws IOException {
		if (randomAccessFile != null) {
			randomAccessFile.close();
		}
	}

}

 

Ads help maintain this website.