// $Id: MultiArrayImpl.java,v 1.3 2003-02-03 20:09:04 donm Exp $ /* * Copyright 1997-2000 Unidata Program Center/University Corporation for * Atmospheric Research, P.O. Box 3000, Boulder, CO 80307, * support@unidata.ucar.edu. * * This library 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.1 of the License, or (at * your option) any later version. * * This library 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 this library; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package ucar.multiarray; import java.lang.reflect.Array; import java.io.IOException; import java.io.Serializable; /** * A concrete, space efficent implementation of the MultiArray interface. * * @see MultiArray * * @author $Author: donm $ * @version $Revision: 1.3 $ $Date: 2003-02-03 20:09:04 $ */ public class MultiArrayImpl implements MultiArray, Cloneable, Serializable { /** * Used to figure out how storage is required for a * given shape. * Compute the right to left multiplicative product of the * argument. * @return int product of the dimensions. * @param dimensions the shape. */ static public int numberOfElements(int [] dimensions) { int product = 1; for(int ii = dimensions.length -1; ii >= 0; ii--) { product *= dimensions[ii]; } return product; } /** * Used to figure out how storage is required for a * given shape, retaining intermediate products. * Compute the right to left multiplicative product of the first * argument, modifying the second argument so that * it contains the intermediate products. * @return int product of the dimensions. * @param dimensions the shape. * @param products modified upon return to contain * the intermediate products */ static public int numberOfElements(int [] dimensions, int [] products) { int product = 1; for(int ii = dimensions.length -1; ii >= 0; ii--) { final int thisDim = dimensions[ii]; if(thisDim < 0) throw new NegativeArraySizeException(); products[ii] = product; product *= thisDim; } return product; } /** * Create a new MultiArray of the given componentType and shape. * Storage for the values is allocated and owned by this with * default initialization. * @param componentType Class of the primitives or objects to * be contained. * @param dimensions the shape of the MultiArray. * dimensions.length determines the rank of the new MultiArray. */ public MultiArrayImpl(Class componentType, int [] dimensions) { lengths = (int []) dimensions.clone(); products = new int[dimensions.length]; int product = numberOfElements(dimensions, products); /* * Use array of length 1 for scalar storage */ if(product == 0) product = 1; storage = Array.newInstance(componentType, product); } /** * A copy constructor. * <p> * Create a new MultiArray with the same componentType and shape * as the argument * Storage for values is allocated and owned by this, and the values * are initialized to the values of the argument. * @param ma the MultiArray to copy. */ public MultiArrayImpl(MultiArray ma) throws IOException { lengths = (int []) ma.getLengths().clone(); products = new int[lengths.length]; int product = numberOfElements(lengths, products); /* * Use array of length 1 for scalar storage */ if(product == 0) product = 1; storage = Array.newInstance(ma.getComponentType(), product); IndexIterator odo = new IndexIterator(lengths); for(; odo.notDone(); odo.incr()) { final int [] index = odo.value(); this.set(index, ma.get(index)); } } /** * Create a new MultiArrayImpl of the given shape accessing * externally created storage. It is up to the client to * to mitigate conflicting access to the external storage. * * @param lengths the shape of the MultiArray. * @param storage array Object which is storage */ public MultiArrayImpl(int [] lengths, Object storage) { this.lengths = lengths; this.products = new int[lengths.length]; final int length = numberOfElements(this.lengths, this.products); if(length > Array.getLength(storage)) throw new IllegalArgumentException( "Inadequate storage"); this.storage = storage; } /** * Create a new MultiArrayImple of the given shape accessing * externally created storage. It is up to the client to * to mitigate conflicting access to the external storage. * Should be a protected constructor? * @param lengths describing the shape of the MultiArray. * @param products right-to-left accumulated sizes. * @param storage array Object which is storage */ public MultiArrayImpl(int [] lengths, int [] products, Object storage) { this.lengths = lengths; this.products = products; this.storage = storage; } /* MultiArray Inquiry methods from MultiArrayInfo */ /** * @see MultiArrayInfo#getComponentType */ public Class getComponentType() { return storage.getClass().getComponentType(); } /** * @see MultiArrayInfo#getRank */ public int getRank() { return lengths.length;} /** * @see MultiArrayInfo#getLengths */ public int [] getLengths() { return (int []) lengths.clone(); } /** * @see MultiArrayInfo#isUnlimited */ public boolean isUnlimited() { return false; } /** * @see MultiArrayInfo#isScalar */ public boolean isScalar() { return 0 == getRank(); } /* End MultiArrayInfo */ /* MultiArray Access methods from Accessor */ /** * @see Accessor#get */ public Object get(int [] index) { return Array.get(storage, indexMap(index)); } /** * @see Accessor#getBoolean */ public boolean getBoolean(int[] index) { return Array.getBoolean(storage, indexMap(index)); } /** * @see Accessor#getChar */ public char getChar(int[] index) { return Array.getChar(storage, indexMap(index)); } /** * @see Accessor#getByte */ public byte getByte(int[] index) { return Array.getByte(storage, indexMap(index)); } /** * @see Accessor#getShort */ public short getShort(int[] index) { return Array.getShort(storage, indexMap(index)); } /** * @see Accessor#getInt */ public int getInt(int[] index) { return Array.getInt(storage, indexMap(index)); } /** * @see Accessor#getLong */ public long getLong(int[] index) { return Array.getLong(storage, indexMap(index)); } /** * @see Accessor#getFloat */ public float getFloat(int[] index) { return Array.getFloat(storage, indexMap(index)); } /** * @see Accessor#getDouble */ public double getDouble(int[] index) { return Array.getDouble(storage, indexMap(index)); } /** * @see Accessor#set */ public void set(int [] index, Object value) { Array.set(storage, indexMap(index), value); } /** * @see Accessor#setBoolean */ public void setBoolean(int [] index, boolean value) { Array.setBoolean(storage, indexMap(index), value); } /** * @see Accessor#setChar */ public void setChar(int [] index, char value) { Array.setChar(storage, indexMap(index), value); } /** * @see Accessor#setByte */ public void setByte(int [] index, byte value) { Array.setByte(storage, indexMap(index), value); } /** * @see Accessor#setShort */ public void setShort(int [] index, short value) { Array.setShort(storage, indexMap(index), value); } /** * @see Accessor#setInt */ public void setInt(int [] index, int value) { Array.setInt(storage, indexMap(index), value); } /** * @see Accessor#setLong */ public void setLong(int [] index, long value) { Array.setLong(storage, indexMap(index), value); } /** * @see Accessor#setFloat */ public void setFloat(int [] index, float value) { Array.setFloat(storage, indexMap(index), value); } /** * @see Accessor#setDouble */ public void setDouble(int[] index, double value) { Array.setDouble(storage, indexMap(index), value); } /** * @see Accessor#copyout */ public MultiArray copyout(int [] origin, int [] shape) { if(origin.length != lengths.length || shape.length != lengths.length) throw new IllegalArgumentException("Rank Mismatch"); // else int ji = lengths.length -1 ; for(; ji >= 0; ji--) { if(origin[ji] != 0 || shape[ji] != lengths[ji]) break; } if(ji < 0) { // origin is zero vector && target shape same as this return (MultiArrayImpl) this.clone(); } // else // ji is the index where a maximum contiguous copy can occur final int [] shp = (int []) shape.clone(); final int [] pducts = new int[shp.length]; final int product = numberOfElements(shp, pducts); final Object dst = Array.newInstance(getComponentType(), product); int src_pos = indexMap(origin); if(ji == 0) { // No loop required System.arraycopy(storage, src_pos, dst, 0, product); } else { ji--; final int step = products[ji]; final int contig = pducts[ji]; for(int dst_pos = 0; dst_pos < product; dst_pos += contig) { System.arraycopy(storage, src_pos, dst, dst_pos, contig); src_pos += step; } } return new MultiArrayImpl(shp, pducts, dst); } /** * Version <code>copyin</code> specialized and optimized for * MultiArrayImpl. * * @see Accessor#copyin */ public void copyin(int [] origin, MultiArrayImpl src) { if(origin.length != lengths.length || src.getRank() != lengths.length) throw new IllegalArgumentException("Rank Mismatch"); // else int ji = lengths.length -1 ; for(; ji >= 0; ji--) { if(origin[ji] != 0 || src.lengths[ji] != lengths[ji]) break; } if(ji < 0) { // origin is zero vector && src shape same as this System.arraycopy(src.storage, 0, storage, 0, Array.getLength(storage)); return; } // else // ji is the index where a maximum contiguous copy can occur int dst_pos = indexMap(origin); if(ji == 0) { // No loop required System.arraycopy(src.storage, 0, storage, dst_pos, Array.getLength(storage) - dst_pos); return; } // else { ji--; final int step = products[ji]; final int contig = src.products[ji]; final int src_length = Array.getLength(src.storage); for(int src_pos = 0; src_pos < src_length; src_pos += contig) { System.arraycopy(src.storage, src_pos, storage, dst_pos, contig); dst_pos += step; } } } /** * @see Accessor#copyin */ public void copyin(int [] origin, MultiArray data) throws IOException { if(data instanceof MultiArrayImpl) { copyin(origin, (MultiArrayImpl)data); return; } // else if(origin.length != lengths.length || data.getRank() != lengths.length) throw new IllegalArgumentException("Rank Mismatch"); // else if(data.getComponentType() != getComponentType()) throw new ArrayStoreException(); // else AbstractAccessor.copy(data, data.getLengths(), this, origin); } static public Object fixDest(Object dst, int lengthNeeded, Class defaultComponentType) { if(dst == null || Array.getLength(dst) < lengthNeeded) { final Class ct = (dst == null ? defaultComponentType : dst.getClass().getComponentType()); dst = Array.newInstance( ct, lengthNeeded); } return dst; } /** * @see Accessor#getStorage */ public Object getStorage() { return storage; } /** * @see Accessor#toArray */ public Object toArray() { // Clone storage. Would // storage.getClass().getDeclaredMethod("clone", new Class [0]) // .invoke(storage, new Object [0]) // be better? final int length = Array.getLength(storage); final Object dst = Array.newInstance(getComponentType(), length); System.arraycopy(storage, 0, dst, 0, length); return dst; } /** * @see Accessor#toArray */ public Object toArray(Object dst, int [] origin, int [] shape) { if(origin.length != lengths.length || shape.length != lengths.length) throw new IllegalArgumentException("Rank Mismatch"); // else int ji = lengths.length -1 ; for(; ji >= 0; ji--) { if(origin[ji] != 0 || shape[ji] != lengths[ji]) break; } if(ji < 0) { final int length = Array.getLength(storage); dst = fixDest(dst, length, getComponentType()); System.arraycopy(storage, 0, dst, 0, length); return dst; } // else // ji is the index where a maximum contiguous copy can occur final int [] shp = (int []) shape.clone(); final int [] pducts = new int[shp.length]; final int product = numberOfElements(shp, pducts); dst = fixDest(dst, product, getComponentType()); int src_pos = indexMap(origin); if(ji == 0) { // No loop required System.arraycopy(storage, src_pos, dst, 0, product); return dst; } // else ji--; final int step = products[ji]; final int contig = pducts[ji]; for(int dst_pos = 0; dst_pos < product; dst_pos += contig) { System.arraycopy(storage, src_pos, dst, dst_pos, contig); src_pos += step; } return dst; } /* End Accessor */ /** * @see java.lang.Object#clone */ public Object clone() { return new MultiArrayImpl((int [])lengths.clone(), (int [])products.clone(), toArray()); } /****/ /** * Convert index vector into integer index into storage. */ public int indexMap(int [] index) { int value = 0; for(int ii = 0; ii < lengths.length; ii++) { final int thisIndex = index[ii]; if( thisIndex < 0 || thisIndex >= lengths[ii]) throw new ArrayIndexOutOfBoundsException(); value += thisIndex * products[ii]; } return value; } /** * The actual storage. An array of componentType. * This member is exposed so that System.arraycopy(), etc * can be used directly on the storage. * @serial */ public final Object storage; /** * Right to left products used in indexMap() to compute * offset into the array. * When incrementing index[ii], one jumps through storage by * products[ii]. * @serial */ private final int[] products; /** * @serial */ private final int[] lengths; /* Begin Test */ public static void main(String[] args) { final int [] shape = {48, 64}; MultiArrayImpl src = new MultiArrayImpl(Integer.TYPE, shape); { final int size = MultiArrayImpl.numberOfElements(shape); for(int ii = 0; ii < size; ii++) java.lang.reflect.Array.setInt(src.storage, ii, ii); } int [] clip = new int[] {32, 64}; int [] origin = new int[] {8, 0}; MultiArray ma = src.copyout(origin, clip); try { System.out.println("Rank " + ma.getRank()); int [] lengths = ma.getLengths(); System.out.println("Shape { " + lengths[0] + ", " + lengths[1] + " }"); System.out.println(ma.getInt(new int[] {0, 0})); System.out.println(ma.getInt(new int[] {1, 0})); System.out.println(ma.getInt(new int[] {lengths[0] -1, lengths[1] -1})); } catch (java.io.IOException ee) {} clip = new int[] {48, 48}; origin = new int[] {0, 8}; ma = src.copyout(origin, clip); try { System.out.println("Rank " + ma.getRank()); int [] lengths = ma.getLengths(); System.out.println("Shape { " + lengths[0] + ", " + lengths[1] + " }"); System.out.println(ma.getInt(new int[] {0, 0})); System.out.println(ma.getInt(new int[] {1, 0})); System.out.println(ma.getInt(new int[] {lengths[0] -1, lengths[1] -1})); } catch (java.io.IOException ee) {} MultiArrayImpl dest = new MultiArrayImpl(Integer.TYPE, shape); try { dest.copyin(origin, ma); System.out.println("***Rank " + dest.getRank()); int [] lengths = dest.getLengths(); System.out.println("Shape { " + lengths[0] + ", " + lengths[1] + " }"); System.out.println(dest.getInt(new int[] {0, 0})); System.out.println(dest.getInt(new int[] {0, 7})); System.out.println(dest.getInt(new int[] {0, 8})); System.out.println(dest.getInt(new int[] {47, 55})); System.out.println(dest.getInt(new int[] {47, 56})); System.out.println(dest.getInt(new int[] {47, 63})); } catch (java.io.IOException ee) {} } /* End Test */ }