View Javadoc
1   /*
2    * Copyright 2009 Ange Optimization ApS
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package eu.simuline.octave.type.matrix;
17  
18  import eu.simuline.octave.type.OctaveObject;
19  import eu.simuline.octave.type.OctaveDouble;
20  
21  import java.util.Arrays;
22  import java.util.List;
23  
24  /**
25   * A general matrix that does not even know 
26   * that it is an array it stores its  in.
27   * 
28   * @param <D>
29   *    an array type, partially of primitive element type 
30   * @param <L> 
31   *    a list corresponding with the array of type D
32   */
33  // used as superclass of classes of this package only 
34  public abstract class AbstractGenericMatrix<D, L extends List<?>> 
35      implements OctaveObject { //, E
36  
37      private static final int PRIME = 31;
38  
39      /**
40       * The dimensions, rows x columns x depth x ....
41       */
42      @SuppressWarnings("checkstyle:visibilitymodifier")
43      protected final int[] size;
44  
45      /**
46       * The data, vectorized.
47       */
48      @SuppressWarnings("checkstyle:visibilitymodifier")
49       protected L dataL;//final 
50  
51      /**
52       * Constructor that creates new blank matrix. 
53       * 
54       * @param size
55       */
56      protected AbstractGenericMatrix(final int... size) {
57          this.size = size.clone();
58          checkSize();
59  	int size1 = product(size);
60  	this.dataL = newL(size1);
61      }
62  
63      /**
64       * Constructor that reuses data in the new object. 
65       * 
66       * @param dataA
67       *    data as an array 
68       * @param size
69       *    must have at least two dimensions 
70       */
71      protected AbstractGenericMatrix(D dataA, int... size) { //List<E> dataL, 
72          this.size = size;
73          checkSize();
74  	int dataLength = initL(dataA, product(size));
75  	checkDataSize(dataLength);
76      }
77  
78      /**
79       * Copy constructor. 
80       * 
81       * @param o
82       */
83      protected AbstractGenericMatrix(final AbstractGenericMatrix<D, L> o) { //, E
84          this.size = o.size.clone();
85  	int size1 = product(this.size);
86  	initL(o.getDataA(), size1);
87      }
88  
89      /**
90       * Checks the field {@link #size}: dimension at lest two 
91       * and each entry non-negative. 
92       */
93      private void checkSize() throws IllegalArgumentException {
94  	switch (this.size.length) {
95  	case 0:
96  	    throw new IllegalArgumentException("no size");
97  	case 1:
98  	    throw new IllegalArgumentException
99  		("size must have a least 2 dimensions");
100 	default:
101 	    for (final int s : this.size) {
102 		// **** what does s=0 mean?
103 		if (s < 0) {
104 		    throw new IllegalArgumentException
105 			("element in size less than zero. =" + s);
106 		}
107 	    }
108 	}
109     }
110 
111     /**
112      * Check that the overall size given by the product of {@link #size} 
113      * does not exceed the length of the backed array. 
114      */
115     private void checkDataSize(int dataLength) {
116         if (product(this.size) > dataLength) {
117             final StringBuilder text = new StringBuilder();
118             text.append("length of data(");
119             text.append(dataLength);
120             text.append(") is smaller than size([");
121             boolean first = true;
122             for (final int i : this.size) {
123                 if (first) {
124                     first = false;
125                 } else {
126                     text.append(", ");
127                 }
128                 text.append(i);
129             }
130             text.append("])");
131             throw new IllegalArgumentException(text.toString());
132         }
133     }
134 
135     /**
136      * Returns a new data store with given size 
137      * and entries carrying the default value. 
138      * The latter depends on the types: false for boolean, 
139      * 0 for int, 0.0 for double and null for GenericMatrix's. 
140      * 
141      * @param size
142      * @return new D[size]
143      */
144     protected abstract L newL(int size);
145 
146     // as a side effect sets dataL and returns length of array 'data'
147     protected abstract int initL(D data, int size);
148 
149     /**
150      * The number of data entries. 
151      * Note that it is heavy load to compute this at the moment. 
152      */
153     public final int dataSize() {
154  	return this.dataL.size();
155     }
156 
157     /**
158      * Returns the data store as an array. 
159      * There are subclasses with array of primitive types. 
160      */
161     protected abstract D getDataA();
162 
163     /**
164      * Sets the entry with plain position <code>pos</code> 
165      * to value parsing the string <code>value</code>. 
166      * Note that this base class cannot provide setter methods 
167      * for java's primitive data types. 
168      *
169      * @param value
170      *    
171      * @param pos
172      *    see e.g. {@link AbstractObjectMatrix#setPlain(String, int)} 
173      *    and {@link OctaveDouble#setPlain(String, int)} 
174      */
175     // throws UnsupportedOperationException for GenericMatrix by default 
176     // design ok? **** 
177     public abstract void setPlain(String value, int pos);
178 
179     /**
180      * @param ns
181      * @return product of ns
182      */
183     private static int product(final int... ns) {
184         int p = 1;
185         for (final int n : ns) {
186 	    // **** here a check against overflow is required. 
187             p *= n;
188         }
189         return p;
190     }
191 
192     /**
193      * Resize matrix up to include pos if necessary, 
194      * i.e. if an entry of <code>pos</code> is greater 
195      * than the according entry in {@link #size}. 
196      * 
197      * @param pos
198      *    an index vector with same dimension as {@link #size} 
199      * @throws UnsupportedOperationException
200      *   if <code>pos</code> has dimension other than that of {@link #size}. 
201      */
202     // could be protected if not used in OctaveComplex
203     public final void resizeUp(final int... pos) {
204         if (this.size.length != pos.length) {
205             throw new UnsupportedOperationException
206 		("Change in number of dimensions not supported (" + size.length
207 		 + "!=" + pos.length + ")");
208 	}
209 
210 	if (this.size.length == 0) {
211 	    // no entry and thus no resize 
212 	    return;
213 	}
214 
215 	int[] orgSize = this.size.clone();
216 	boolean resizeNeeded = false;
217 	for (int idx = 0; idx < this.size.length; idx++) {
218 	    if (pos[idx] > this.size[idx]) {
219 		this.size[idx] = pos[idx];
220 		resizeNeeded = true;
221 	    }
222 	}
223 	// Here, this.size is updated whereas orgSize contains original size
224 
225 	if (!resizeNeeded) {
226 	    return;
227 	}
228 
229 	// Here, orgSize[0] is defined 
230 	final int cpyLen    = orgSize[0];
231 	final int osp = product(orgSize);
232 
233 	// initialize resulting array with default values 
234 	D dataInL  = getDataA();
235 	this.dataL = newL(product(this.size));
236 	int idxSrc = 0;
237 	int idxTrg = 0;
238 	int[] idxTrgMulti = new int[orgSize.length]; // 0th entry not used 
239 	int idxIdx;
240 	int lenUnitGap;
241 	while (idxSrc < osp) {
242 	    System.arraycopy(dataInL, idxSrc, 
243 			     getDataA(), idxTrg, cpyLen);
244 	    idxSrc += cpyLen;
245 
246 	    // update idxTrgMulti and idxTrg 
247 	    idxIdx = 1;
248 	    lenUnitGap = this.size[0];
249 	    while (idxIdx < orgSize.length 
250 	    	   &&  idxTrgMulti[idxIdx] >= orgSize[idxIdx] - 1) {
251 	    	assert idxTrgMulti[idxIdx] == orgSize[idxIdx] - 1;
252 
253 		idxTrg -= lenUnitGap * idxTrgMulti[idxIdx];
254 	    	idxTrgMulti[idxIdx] = 0;
255 
256 		lenUnitGap *= this.size[idxIdx];
257 	    	idxIdx++;
258 	    }
259 	    assert idxIdx == orgSize.length 
260 	    	||  idxTrgMulti[idxIdx] < orgSize[idxIdx];
261 //	    assert idxIdx < orgSize.length;
262 	    if (idxIdx >= orgSize.length) {
263 		break;
264 	    }
265 	    // update top 
266 
267 	    idxTrg += lenUnitGap;
268 	    idxTrgMulti[idxIdx]++;
269 	} // while 
270     }
271 
272     /**
273      * @param pos
274      * @return the index into data() for the position
275      */
276     public final int pos2ind(final int... pos) {
277         int idx = 0;
278         int factor = 1;
279         for (int dim = 0; dim < pos.length; ++dim) {
280             if (pos[dim] > this.size[dim]) {
281                 throw new IndexOutOfBoundsException
282 		    ("pos exceeded dimension for dimension " + 
283 		     dim + " (" + pos[dim] + " > " + this.size[dim] + ")");
284             }
285             idx += (pos[dim] - 1) * factor;
286             factor *= this.size[dim];
287         }
288         return idx;
289     }
290 
291     /**
292      * Returns the string representation of the given plain position. 
293      */
294     public abstract String getPlainString(int pos);
295 
296     public final int getSizeLength() {
297         return this.size.length;
298     }
299 
300     /**
301      * @param i
302      *            dimension number in 1 based numbering, 1=row, 2=column
303      * @return the size in dimension i
304      */
305     public final int getSize(final int i) {
306         return this.size[i - 1];
307     }
308 
309     @Override
310     public final int hashCode() {
311         int result = 1;
312         result = PRIME * result + 
313 	    ((this.dataL == null) ? 0 : this.dataL.hashCode());
314         result = PRIME * result + Arrays.hashCode(this.size);
315         return result;
316     }
317 
318     @SuppressWarnings("unchecked")
319     @Override
320     public final boolean equals(final Object obj) {
321         if (this == obj) {
322             return true;
323         }
324         if (obj == null || getClass() != obj.getClass()) {
325             return false;
326         }
327         final AbstractGenericMatrix<D, List<?>> other = 
328 	    (AbstractGenericMatrix<D, List<?>>) obj;
329         if (!Arrays.equals(this.size, other.size)) {
330 	    return false;
331         }
332 	
333 	assert this.dataL.size() == product(this.size);
334 	return this.dataL.equals(other.dataL);
335     }
336 
337     // to implement OctaveObject 
338     public abstract OctaveObject shallowCopy();
339 
340     public static void main(String[] args) {
341 	System.out.println("R" + product());
342     }
343 
344 }