All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.meliorbis.numerics.Numerics Maven / Gradle / Ivy

Go to download

A library for working with large multi-dimensional arrays and the functions they represent

There is a newer version: 1.2
Show newest version
/**
 * 
 */
package com.meliorbis.numerics;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.PrimitiveIterator;
import java.util.logging.Logger;

import com.meliorbis.numerics.generic.MultiDimensionalArray;
import com.meliorbis.numerics.generic.MultiDimensionalArrayException;
import com.meliorbis.numerics.generic.MultiDimensionalArrayFactory;
import com.meliorbis.numerics.generic.SettableIndexedIterator;
import com.meliorbis.numerics.generic.impl.IntegerArray;
import com.meliorbis.numerics.generic.impl.IntegerArrayFactory;
import com.meliorbis.numerics.generic.primitives.DoubleSettableIndexedIterator;
import com.meliorbis.numerics.generic.primitives.DoubleSettableIterator;
import com.meliorbis.numerics.generic.primitives.impl.DoubleArray;
import com.meliorbis.numerics.generic.primitives.impl.DoubleBlockedArrayFactory;
import com.meliorbis.numerics.threading.CurrentThreadExecutor;
import com.meliorbis.numerics.threading.Executor;
import com.meliorbis.numerics.threading.MultiThreadedExecutor;
import com.meliorbis.utils.Utils;

import au.com.bytecode.opencsv.CSVReader;

/**
 * Facade for the numerics system that is configurable to provide different
 * types of array etc.
 * 
 * @author Tobias Grasl
 * 
 * @param  The numeric type to be used
 */
public class Numerics
{
    private static final int THREAD_COUNT = Integer.getInteger("com.meliorbis.numerics.threads", 5);
    
    public static final Logger LOG = Logger.getLogger(Numerics.class.getName());
    
    private static volatile Numerics _instance;
    
    public static Numerics instance()
    {
    	Numerics instance = _instance;
    	
    	if(instance == null)
    	{
    		LOG.warning("Accessing Numerics instance before it is initialised, defaulting to DoubleNumerics");
    		
    		instance = DoubleNumerics.instance();
    		
    		_instance = instance;
    	}
    	
    	return instance;
    }
    
    private final Executor _executor;
	private final DoubleBlockedArrayFactory _arrayFactory;
	
	@SuppressWarnings("rawtypes")
	final private HashMap _factoriesByClass = new HashMap();
	
	public Numerics(Class class_)
	{
		this(class_, THREAD_COUNT);
	}

	public Numerics(Class class_, int threadCount_)
	{
		_executor = threadCount_ == 1 ? new CurrentThreadExecutor() : new MultiThreadedExecutor(threadCount_);
		
		if(class_.equals(Double.class))
		{
			_arrayFactory =  new DoubleBlockedArrayFactory(_executor/*new CurrentThreadExecutor()*/);
		}
		else
		{
			throw new IllegalArgumentException("Presently, only double numerics is supported");
		}
		
		_factoriesByClass.put(class_,_arrayFactory);
		_factoriesByClass.put(Integer.class,new IntegerArrayFactory(this));
	}

	public void destroy()
	{
		_executor.destroy();
	}

	/**
	 * @return the arrayFactory
	 */
	public DoubleBlockedArrayFactory getArrayFactory()
	{
		return _arrayFactory;
	}

	/**
	 * Reads a CSV file at the specified path into a two-dimensional array, which is returned.
	 * 
	 * @param path_ The path from which to read the file
	 * 
	 * @return A 2-D array with the same numbers of rows and columns as the CSV file
	 */
	public DoubleArray readCSV(String path_)
	{
		return readCSV(path_, ',');
	}
	
	/**
	 * Reads a CSV resource from the provided input stream into a two-dimensional array, which is returned.
	 * 
	 * @param is_ The stream from which to read the data
	 * 
	 * @return A 2-D array with the same numbers of rows and columns as the CSV data
	 */
	public DoubleArray readCSV(InputStream is_)
	{
		return readCSV(is_, ',');
	}

	/**
	 * Writes the provided array to a CSV file that also contains metadata so that the original array
	 * can be reproduced from the file.
	 * 
	 * @param array_ The array to be written
	 * @param path_ The path at which to save the written array
	 * 
	 * @param  The type of numeric array to be written
	 */
	public  void writeFormattedCSV(MultiDimensionalArray array_, String path_)
	{
		writeFormattedCSV(array_, path_,',');
	}
	
	
	/**
	 * Writes the provided array to a CSV file that also contains metadata so that the original array
	 * can be reproduced from the file.
	 * 
	 * @param array_ The array to be written
	 * @param path_ The path at which to save the written array
	 * 
	 * @param  The type of numeric array to be written
	 */
	public  void writeFormattedCSV(MultiDimensionalArray array_, File path_)
	{
		writeFormattedCSV(array_, path_, ',');
	}
	
	/**
	 * Writes the provided array to a CSV file that also contains metadata so that the original array
	 * can be reproduced from the file. The file will use the provided separator between fields
	 * 
	 * @param array_ The array to be written
	 * @param path_ The path at which to save the written array
	 * @param separator_ The field-separator to use
	 * 
	 * @param  The type of numeric array to be written
	 */
	public  void writeFormattedCSV(MultiDimensionalArray array_, String path_, char separator_)
	{
		writeFormattedCSV(array_, new File(path_), separator_);
	}

	/**
	 * Writes the provided array to a CSV file that also contains metadata so that the original array
	 * can be reproduced from the file. The file will use the provided separator between fields
	 * 
	 * @param array_ The array to be written
	 * @param file_ The path at which to save the written array
	 * @param separator_ The field-separator to use
	 */
	private  void writeFormattedCSV(MultiDimensionalArray array_, File file_,
			char separator_) 
	{
		FileWriter writer = null;
		
		try {
			writer = new FileWriter(file_);
			
			writeFormattedCSV(array_, separator_, writer);
			
		} catch (IOException e) {
			throw new NumericsException("Error writing CSV", e);
		}
		finally
		{
			if(writer != null)
			{
				try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private  void writeFormattedCSV(MultiDimensionalArray array_,
			char separator_, FileWriter writer) 
	{
		try {

            if(array_ == null)
            {
                LOG.warning("Tried to write null array. Ignoring.");
                return;
            }

			// Write the array size in the first row
			int[] size = array_.size();
			
			// Write the class of the array by using the first element
			// TODO: This means that zero-size arrays can't be written

            String type = array_ instanceof DoubleArray ? Double.class.getName() :
                                                                Integer.class.getName();

            boolean isDouble = array_ instanceof DoubleArray;
            
			writer.write(type);
			
			// Don't forget the separator!
			writer.write(separator_);
			
			for (int i = 0; i < size.length; i++) {
				writer.write(Integer.toString(size[i]));
				
				if(i < (size.length-1))
				{
					writer.write(separator_);
				}
			}
			
			// Now write the array
			SettableIndexedIterator arrayIter = array_.iterator();
			
			/* Write the array in one big column!
			 */
			while(arrayIter.hasNext())
			{
				// Add a line break
				writer.write('\n');
				
				// Write the next array entry
				if(isDouble)
				{
					writer.write(Double.toString(((PrimitiveIterator.OfDouble)arrayIter).nextDouble()));
				}
				else
				{
					writer.write(arrayIter.next().toString());
				}
				
			}
			
			writer.flush();
		} catch (IOException e) {
			throw new NumericsException("Error writing array to file",e);
		} catch (MultiDimensionalArrayException e) {
			throw new NumericsException("Error writing array to file",e);
		}
	}
	

	/**
	 * Reads an array from a CSV file that also contains the relevant metdata to reproduce the correct dimensions
	 * 
	 * @param file_ The file from which to read the array
	 * 
	 * @param  The type of numeric array to be read
	 * 
	 * @return The array read from the file
	 */
	public  MultiDimensionalArray readFormattedCSV(File file_)
	{
		FileReader fileReader = null;
		try
		{
			fileReader = new FileReader(file_);
			return readFormattedCSV(fileReader);
		} catch (FileNotFoundException e)
		{
			throw new NumericsException(e);
		} catch (IOException e)
		{
			throw new NumericsException(e);
		}
		finally
		{
			if(fileReader != null)
			{
				try {
					fileReader.close();
				} catch (IOException e) {
					System.out.println("Unable to close reader");
				}
			}
		}
	}
	
	/**
	 * Reads an array from a CSV file that also contains the relevant metdata to reproduce the correct dimensions
	 * 
	 * @param path_ Path to the file from which to read the array
	 * 
	 * @param  The type of numeric array to be read
	 * 
	 * @return The array read from the file
	 */
	public  com.meliorbis.numerics.generic.MultiDimensionalArray readFormattedCSV(String path_) 
	{	
		return readFormattedCSV(new File(path_));
	}
	
	/**
	 * Reads an array from a CSV stream that also contains the relevant metadata to reproduce the correct dimensions
	 * 
	 * @param is_ The stream from which to read the array
	 * 
	 * @param  The type of numeric array to be read
	 * 
	 * @return The array read from the file
	 */
	public  com.meliorbis.numerics.generic.MultiDimensionalArray readFormattedCSV(InputStream is_)
	{
		
		try
		{
			return readFormattedCSV(new InputStreamReader(is_));
		} catch (IOException e)
		{
			throw new NumericsException(e);
		}

	}

	private  com.meliorbis.numerics.generic.MultiDimensionalArray readFormattedCSV(
			Reader reader) throws IOException, NumericsException {
		
		CSVReader csvReader = new CSVReader(reader,',');
		
		List allLines = csvReader.readAll();
		csvReader.close();
		
		int rows = allLines.size();
		int columns = 0;

        if(allLines.size() == 0)
        {
            LOG.warning("Tried to read empty file. Ignoring.");
            return null;
        }

		// Determine the dimensions of the matrix from the first row
		String[] dimStrings = allLines.get(0);


        MultiDimensionalArrayFactory> arrayFactory;
		
		try {
			@SuppressWarnings("unchecked")
			Class arrayClass = (Class) Class.forName(dimStrings[0]);
			
			arrayFactory = getArrayFactory(arrayClass);
		} catch (ClassNotFoundException e) {
			throw new NumericsException("Badly formed file, unknown array type",e);
		}
		
		int[] dims = new int[dimStrings.length-1];
		
		for (int i = 0; i < dims.length; i++) {
			dims[i] = Integer.parseInt(dimStrings[i+1]);
		}
		
		if(rows > 1)
		{
			columns = allLines.get(1).length;
		}
		
		if(columns == 0)
		{
			throw new NumericsException("Badly formed file has no rows or columns");
		}
						
		MultiDimensionalArray parsedArray;

		parsedArray = arrayFactory.newArray(dims);
		
		SettableIndexedIterator arrayIterator = parsedArray.iterator();
		
		int rowIndex = 1;
		int colIndex = 0;
		
		for(; rowIndex < rows; rowIndex++)
		{
			
			String[] currentRow = allLines.get(rowIndex);
			if(currentRow.length != columns)
			{
				throw new NumericsException("Not all rows the same length");
			}
			
			colIndex = 0;
			
			for(; colIndex < columns && arrayIterator.hasNext(); colIndex++)
			{
				if(arrayIterator instanceof PrimitiveIterator.OfDouble)
				{
					((PrimitiveIterator.OfDouble)arrayIterator).nextDouble();
				}
				else
				{
					arrayIterator.next();
				}
				
				V value = arrayFactory.fromString(currentRow[colIndex]);
				
				if(arrayIterator instanceof PrimitiveIterator.OfDouble)
				{
					((DoubleSettableIterator)arrayIterator).set(((Double)value).doubleValue());
				}
				else
				{
					arrayIterator.set(value);
				}
			}
			
			if(colIndex < columns)
			{
				break;
			}
		}
		
		if(arrayIterator.hasNext())
		{
			System.err.println("Less data than space in the array!");
		}
		else if(rowIndex < rows || colIndex < columns)
		{
			System.err.println("More data than space in the array!");
		}

		return parsedArray;
	}
	
	
	/**
	 * Retrieves the factory class for creating arrays of the specified type
	 * 
	 * @param clazz_ The type of numeric array for which to get the factory
	 * 
	 * @param  The type of numeric array for which to get the factory
	 * 
	 * @return The array factory for the type requested
	 */
	@SuppressWarnings("unchecked")
	public  MultiDimensionalArrayFactory> getArrayFactory(Class clazz_)
	{
		return _factoriesByClass.get(clazz_);
	}
	

	/**
	 * Reads a CSV file at the specified path into a two-dimensional array, which is returned
	 * 
	 * @param path_ The path from which to read the file
	 * @param sep_ The separator used in the file
	 * 
	 * @return A 2-D array with the same numbers of rows and columns as the CSV file
	 */
	public DoubleArray readCSV(String path_, char sep_)
	{
		FileInputStream is = null;
		
		try
		{
			is = new FileInputStream(path_);
		
			return readCSV(is, sep_);
		} catch (FileNotFoundException e)
		{
			throw new NumericsException(e);
		}
		finally
		{
			if(is != null)
			{
				try
				{
					is.close();
				} catch (IOException e){}
			}
		}
	}

	/**
	 * Reads a CSV resource from the specified input stream into a two-dimensional array, which is returned
	 * 
	 * @param is_ The input stream to read
	 * @param sep_ The separator to use
	 * 
	 * @return A two-dimensional array containing the data from the CSV resource 
	 */
	public DoubleArray readCSV(InputStream is_, char sep_) {
		DoubleArray parsedArray;
		try
		{
			List allLines = readAllCSV(is_, sep_);
			
			int rows = allLines.size();
			int columns = 0;
			
			if(rows > 0)
			{
				columns = allLines.get(0).length;
			}
			
			if(columns == 0)
			{
				throw new NumericsException("Badly formed file has no rows or columns");
			}
							
			parsedArray = getArrayFactory().newArray(rows,columns);

            DoubleSettableIndexedIterator iterator = parsedArray.iterator();

            for(int rowIndex = 0; rowIndex < rows; rowIndex++)
			{
				String[] currentRow = allLines.get(rowIndex);
				if(currentRow.length != columns)
				{
					throw new NumericsException("Not all rows the same length");
				}
				
				for(int colIndex = 0; colIndex < columns; colIndex++)
				{
					double value = getArrayFactory().fromString(currentRow[colIndex]);

                    iterator.nextDouble();
                    iterator.set(value);
				}
			}
		} catch (IOException e)
		{
			throw new NumericsException(e);
		} catch (MultiDimensionalArrayException e)
		{
			throw new NumericsException(e);
		}

		return parsedArray;
	}
	
	/**
	 * Reads a CSV resource from the specified input stream into a two-dimensional array, which is returned
	 * 
	 * @param is_ The input stream to read
	 * @param sep_ The separator to use
	 * 
	 * @return A two-dimensional array containing the data from the CSV resource
	 */
	public IntegerArray readIntCSV(InputStream is_, char sep_)
	{
		IntegerArray parsedArray;
		try
		{
			List allLines = readAllCSV(is_, sep_);
			
			int rows = allLines.size();
			int columns = 0;
			
			if(rows > 0)
			{
				columns = allLines.get(0).length;
			}
			
			if(columns == 0)
			{
				throw new NumericsException("Badly formed file has no rows or columns");
			}
							
			parsedArray =  newIntArray(rows,columns);

            SettableIndexedIterator iterator = parsedArray.iterator();

            for(int rowIndex = 0; rowIndex < rows; rowIndex++)
			{
				String[] currentRow = allLines.get(rowIndex);
				if(currentRow.length != columns)
				{
					throw new NumericsException("Not all rows the same length");
				}
				
				for(int colIndex = 0; colIndex < columns; colIndex++)
				{
					Integer value = Integer.parseInt(currentRow[colIndex]);

                    iterator.next();
                    iterator.set(value);
				}
			}
		} catch (IOException e)
		{
			throw new NumericsException(e);
		} catch (MultiDimensionalArrayException e)
		{
			throw new NumericsException(e);
		}

		return parsedArray;
	}
	
	private List readAllCSV(InputStream is_, char sep_) throws IOException
	{
		Reader isReader = new InputStreamReader(is_);
		CSVReader reader = new CSVReader(isReader,sep_);

		List allLines;
		
		try
		{
			allLines = reader.readAll();
		}
		finally
		{
			reader.close();
		}
		return allLines;
	}
	
	/**
	 * Returns a new array of integers with the given dimensions
	 *
	 * @param dimensions_ The required dimensions of the array
	 *
	 * @return An appropriately sized integer array
	 */
	public IntegerArray newIntArray(List dimensions_)
	{
		return new IntegerArray(getExecutor(), Utils.toIntArray(dimensions_));
	}

	/**
	 * Returns a new array of integers with the given dimensions
	 *
	 * @param dimensions_ The required dimensions of the array
	 *
	 * @return An appropriately sized integer array
	 */
	public IntegerArray newIntArray(int... dimensions_)
	{
		return new IntegerArray(getExecutor(), dimensions_);
	}
	
//	/**
//	 * Returns a new array of integers with the given dimensions
//	 *
//	 * @param dimensions_ The required dimensions of the array
//	 *
//	 * @return An appropriately sized integer array
//	 */
//	public  GenericArray newGenericArray(Class type_, int... dimensions_)
//	{
//		return new GenericArray(type_, getExecutor(), dimensions_ );
//	}

	/**
	 * @return The task executor of this numerics object
	 */
	public Executor getExecutor() {
		return _executor;
	}

}