1 //******************************************************************************
2 //
3 // File: DoubleMatrixFile.java
4 // Package: edu.rit.io
5 // Unit: Class edu.rit.io.DoubleMatrixFile
6 //
7 // This Java source file is copyright (C) 2008 by Alan Kaminsky. All rights
8 // reserved. For further information, contact the author, Alan Kaminsky, at
9 // ark@cs.rit.edu.
10 //
11 // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12 // software; you can redistribute it and/or modify it under the terms of the GNU
13 // General Public License as published by the Free Software Foundation; either
14 // version 3 of the License, or (at your option) any later version.
15 //
16 // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19 //
20 // Linking this library statically or dynamically with other modules is making a
21 // combined work based on this library. Thus, the terms and conditions of the GNU
22 // General Public License cover the whole combination.
23 //
24 // As a special exception, the copyright holders of this library give you
25 // permission to link this library with independent modules to produce an
26 // executable, regardless of the license terms of these independent modules, and
27 // to copy and distribute the resulting executable under terms of your choice,
28 // provided that you also meet, for each linked independent module, the terms
29 // and conditions of the license of that module. An independent module is a module
30 // which is not derived from or based on this library. If you modify this library,
31 // you may extend this exception to your version of the library, but you are not
32 // obligated to do so. If you do not wish to do so, delete this exception
33 // statement from your version.
34 //
35 // A copy of the GNU General Public License is provided in the file gpl.txt. You
36 // may also obtain a copy of the GNU General Public License on the World Wide
37 // Web at http://www.gnu.org/licenses/gpl.html.
38 //
39 //******************************************************************************
40 package edu.rit.io;
41
42 import java.io.BufferedInputStream;
43 import java.io.BufferedOutputStream;
44 import java.io.DataInputStream;
45 import java.io.DataOutputStream;
46 import java.io.EOFException;
47 import java.io.FileInputStream;
48 import java.io.FileOutputStream;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.OutputStream;
52
53 import edu.rit.util.Range;
54
55 /**
56 * Class DoubleMatrixFile provides an object for reading or writing a double
57 * matrix from or to a file. The matrix containing the data to read or write is
58 * a separate object, specified as an argument to the constructor or the
59 * <code>setMatrix()</code> method.
60 * <P>
61 * To write the matrix to a file, call the <code>prepareToWrite()</code> method,
62 * specifying the output stream to write. The <code>prepareToWrite()</code> method
63 * returns an instance of class {@linkplain DoubleMatrixFile.Writer}. Call the
64 * methods of the writer object to write the matrix, or sections of the matrix,
65 * to the output stream. When finished, close the writer.
66 * <P>
67 * To read the matrix from a file, call the <code>prepareToRead()</code> method,
68 * specifying the input stream to read. The <code>prepareToRead()</code> method
69 * returns an instance of class {@linkplain DoubleMatrixFile.Reader}. Call the
70 * methods of the reader object to read the matrix, or sections of the matrix,
71 * from the input stream. When finished, close the reader.
72 * <P>
73 * Class DoubleMatrixFile includes a main program to combine a group of double
74 * matrix files into one double matrix file. You might use this main program
75 * when the processes of a parallel program have computed slices of a matrix and
76 * stored the slices in separate files, and you want to combine the slices
77 * together into one file.
78 * <P>
79 * <B>Double Matrix File Format</B>
80 * <P>
81 * A double matrix file is a binary file containing the following items. Each
82 * primitive item is written as though by java.io.DataOutput (<code>int</code>, four
83 * bytes; <code>double</code>, eight bytes; most significant byte first).
84 * <UL>
85 * <LI>
86 * Number of matrix rows, <I>R</I> (<code>int</code>). <I>R</I> >= 0.
87 * <LI>
88 * Number of matrix columns, <I>C</I> (<code>int</code>). <I>C</I> >= 0.
89 * <LI>
90 * Zero or more segments of matrix elements.
91 * </UL>
92 * <P>
93 * Each segment contains the following items.
94 * <UL>
95 * <LI>
96 * The segment's lower row index, <I>RL</I> (<code>int</code>). <I>RL</I> >= 0.
97 * <LI>
98 * The segment's lower column index, <I>CL</I> (<code>int</code>). <I>CL</I> >=
99 * 0.
100 * <LI>
101 * Number of rows in the segment, <I>M</I> (<code>int</code>). <I>M</I> >= 0.
102 * <I>RL</I>+<I>M</I> <= <I>R</I>.
103 * <LI>
104 * Number of columns in the segment, <I>N</I> (<code>int</code>). <I>N</I> >= 0.
105 * <I>CL</I>+<I>N</I> <= <I>C</I>.
106 * <LI>
107 * The matrix elements in rows <I>RL</I>..<I>RL</I>+<I>M</I>-1 and columns
108 * <I>CL</I>..<I>CL</I>+<I>N</I>-1 (<code>double</code>). Stored in row major order.
109 * </UL>
110 *
111 * @author Alan Kaminsky
112 * @version 06-Jan-2008
113 */
114 public class DoubleMatrixFile {
115
116 // Hidden data members.
117 private static final long BYTES_PER_ELEMENT = 8L;
118
119 private int R = -1;
120 private int C = -1;
121 private double[][] myMatrix;
122
123 // Exported constructors.
124 /**
125 * Construct a new double matrix file object. The matrix's number of rows
126 * and number of columns are uninitialized. Before using the matrix file,
127 * specify the number of rows and the number of columns by calling the
128 * <code>setMatrix()</code> method or by reading the matrix file from an input
129 * stream.
130 */
131 public DoubleMatrixFile() {
132 }
133
134 /**
135 * Construct a new double matrix file object with the given number of rows,
136 * number of columns, and underlying matrix.
137 *
138 * @param R Number of rows.
139 * @param C Number of columns.
140 * @param theMatrix Underlying matrix.
141 * @exception NullPointerException (unchecked exception) Thrown if
142 * <code>theMatrix</code> is null.
143 * @exception IllegalArgumentException (unchecked exception) Thrown if
144 * <code>R</code> < 0. Thrown if
145 * <code>C</code> < 0. Thrown if <code>theMatrix.length</code> does not equal
146 * <code>R</code>.
147 */
148 public DoubleMatrixFile(int R,
149 int C,
150 double[][] theMatrix) {
151 setMatrix(R, C, theMatrix);
152 }
153
154 // Exported operations.
155 /**
156 * Returns the number of rows in this matrix file.
157 *
158 * @return Number of rows <I>R</I>, or -1 if not initialized.
159 */
160 public int getRowCount() {
161 return R;
162 }
163
164 /**
165 * Returns the number of columns in this matrix file.
166 *
167 * @return Number of columns, <I>C</I>, or -1 if not initialized.
168 */
169 public int getColCount() {
170 return C;
171 }
172
173 /**
174 * Obtain this matrix file's underlying matrix.
175 *
176 * @return Underlying matrix, or null if not initialized.
177 */
178 public double[][] getMatrix() {
179 return myMatrix;
180 }
181
182 /**
183 * Set this matrix file's number of rows, number of columns, and underlying
184 * matrix.
185 *
186 * @param R Number of rows.
187 * @param C Number of columns.
188 * @param theMatrix Underlying matrix.
189 * @exception NullPointerException (unchecked exception) Thrown if
190 * <code>theMatrix</code> is null.
191 * @exception IllegalArgumentException (unchecked exception) Thrown if
192 * <code>R</code> < 0. Thrown if
193 * <code>C</code> < 0. Thrown if <code>theMatrix.length</code> does not equal
194 * <code>R</code>.
195 */
196 public void setMatrix(int R,
197 int C,
198 double[][] theMatrix) {
199 setRC(R, C);
200 if (theMatrix.length != R) {
201 throw new IllegalArgumentException("DoubleMatrixFile.setMatrix(): theMatrix.length (= "
202 + theMatrix.length + ") does not equal R (= " + R + ")");
203 }
204 myMatrix = theMatrix;
205 }
206
207 /**
208 * Prepare to write this matrix file to the given output stream. The number
209 * of rows and the number of columns in this matrix file are written to the
210 * output stream at this time. To write this matrix file's elements, call
211 * methods on the returned writer, then close the writer.
212 * <P>
213 * For improved performance, specify an output stream with buffering, such
214 * as an instance of class java.io.BufferedOutputStream.
215 *
216 * @param theStream Output stream.
217 * @return Writer object with which to write this matrix file.
218 * @exception IllegalStateException (unchecked exception) Thrown if this
219 * matrix file object is uninitialized.
220 * @exception NullPointerException (unchecked exception) Thrown if
221 * <code>theStream</code> is null.
222 * @exception IOException Thrown if an I/O error occurred.
223 * @throws java.io.IOException if any.
224 */
225 public Writer prepareToWrite(OutputStream theStream)
226 throws IOException {
227 if (myMatrix == null) {
228 throw new IllegalStateException("DoubleMatrixFile.prepareToWrite(): Not initialized");
229 }
230 return new Writer(theStream);
231 }
232
233 /**
234 * Prepare to read this matrix file from the given input stream. The number
235 * of rows and the number of columns are read from the input stream at this
236 * time. If this matrix file object is uninitialized, it becomes initialized
237 * with the given number of rows and columns, and storage is allocated for
238 * the underlying matrix's row references; call the <code>getMatrix()</code>
239 * method to obtain the underlying matrix. If this matrix file object is
240 * already initialized, the number of rows and columns read from the input
241 * stream must match those of this matrix file object, otherwise an
242 * exception is thrown. To read this matrix file's elements, call methods on
243 * the returned reader, then close the reader.
244 * <P>
245 * For improved performance, specify an input stream with buffering, such as
246 * an instance of class java.io.BufferedInputStream.
247 *
248 * @param theStream Input stream.
249 * @return Reader object with which to read this matrix file.
250 * @exception NullPointerException (unchecked exception) Thrown if
251 * <code>theStream</code> is null.
252 * @exception IOException Thrown if an I/O error occurred.
253 * @exception InvalidMatrixFileException (subclass of IOException) Thrown if
254 * the input stream's contents were invalid.
255 * @throws java.io.IOException if any.
256 */
257 public Reader prepareToRead(InputStream theStream)
258 throws IOException {
259 return new Reader(theStream);
260 }
261
262 /**
263 * Main program to combine a group of double matrix files into one double
264 * matrix file. The program reads all the given input files into a matrix,
265 * then writes the entire matrix to the given output file as a single
266 * segment. Each input file must be for a matrix with the same number of
267 * rows and columns. There must be sufficient main memory to hold the entire
268 * matrix.
269 * <P>
270 * You might use this main program when the processes of a parallel program
271 * have computed slices of a matrix and stored the slices in separate files,
272 * and you want to combine the slices together into one file.
273 * <P>
274 * Usage: java edu.rit.io.DoubleMatrixFile <I>outfile</I> <I>infile1</I>
275 * [ <I>infile2</I> . . . ]
276 *
277 * @param args an array of {@link java.lang.String} objects.
278 * @throws java.lang.Exception if any.
279 */
280 public static void main(String[] args)
281 throws Exception {
282 // Validate command line arguments.
283 if (args.length < 2) {
284 System.err.println("Usage: java edu.rit.io.DoubleMatrixFile <outfile> <infile1> [<infile2> ...]");
285 System.exit(1);
286 }
287
288 // Double matrix file object.
289 DoubleMatrixFile dmf = new DoubleMatrixFile();
290
291 // Read each input file.
292 for (int i = 1; i < args.length; ++i) {
293 DoubleMatrixFile.Reader reader
294 = dmf.prepareToRead(new BufferedInputStream(new FileInputStream(args[i])));
295 reader.read();
296 reader.close();
297 }
298
299 // Write output file.
300 DoubleMatrixFile.Writer writer
301 = dmf.prepareToWrite(new BufferedOutputStream(new FileOutputStream(args[0])));
302 writer.write();
303 writer.close();
304 }
305
306 // Exported helper classes.
307 /**
308 * Class DoubleMatrixFile.Writer provides an object with which to write a
309 * {@linkplain DoubleMatrixFile} to an output stream.
310 * <P>
311 * When a writer is created, the number of rows and number of columns are
312 * written to the output stream. Each time the <code>write()</code>,
313 * <code>writeRowSlice()</code>, <code>writeColSlice()</code>, or
314 * <code>writePatch()</code> method is called, one segment of matrix elements is
315 * written to the output stream. When finished, call the <code>close()</code>
316 * method.
317 * <P>
318 * <I>Note:</I> Class DoubleMatrixFile.Writer is not multiple thread safe.
319 *
320 * @author Alan Kaminsky
321 * @version 06-Jan-2008
322 */
323 public class Writer {
324
325 // Hidden data members.
326 private OutputStream myOs;
327 private DataOutputStream myDos;
328
329 // Hidden constructors.
330 /**
331 * Construct a new double matrix file writer.
332 *
333 * @param theStream Output stream.
334 *
335 * @exception NullPointerException (unchecked exception) Thrown if
336 * <code>theStream</code> is null.
337 * @exception IOException Thrown if an I/O error occurred.
338 */
339 private Writer(OutputStream theStream)
340 throws IOException {
341 if (theStream == null) {
342 throw new NullPointerException("DoubleMatrixFile.Writer(): theStream is null");
343 }
344 myOs = theStream;
345 myDos = new DataOutputStream(theStream);
346 myDos.writeInt(R); // Number of matrix rows
347 myDos.writeInt(C); // Number of matrix columns
348 }
349
350 // Exported operations.
351 /**
352 * Write all rows and columns of the matrix to the output stream.
353 *
354 * @exception IOException Thrown if an I/O error occurred.
355 */
356 public void write()
357 throws IOException {
358 write(0, R - 1, 0, C - 1);
359 }
360
361 /**
362 * Write the given row slice of the matrix to the output stream.
363 * Elements in the given range of rows and in all columns are written.
364 * <P>
365 * <I>Note:</I> <code>theRowRange</code>'s stride must be 1.
366 *
367 * @param theRowRange Range of matrix rows.
368 *
369 * @exception NullPointerException (unchecked exception) Thrown if
370 * <code>theRowRange</code> is null.
371 * @exception IllegalArgumentException (unchecked exception) Thrown if
372 * <code>theRowRange</code>'s stride is greater than 1.
373 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
374 * any index in <code>theRowRange</code>
375 * is outside the range 0 .. <I>R</I>-1.
376 * @exception IOException Thrown if an I/O error occurred.
377 */
378 public void writeRowSlice(Range theRowRange)
379 throws IOException {
380 if (theRowRange.stride() != 1) {
381 throw new IllegalArgumentException("DoubleMatrixImage.Writer.writeRowSlice(): theRowRange stride > 1");
382 }
383 int RL = theRowRange.lb();
384 int RU = theRowRange.ub();
385 if (0 > RL || RL + theRowRange.length() > R) {
386 throw new IndexOutOfBoundsException("DoubleMatrixImage.Writer.writeRowSlice(): theRowRange = "
387 + theRowRange + " out of bounds");
388 }
389 write(RL, RU, 0, C - 1);
390 }
391
392 /**
393 * Write the given column slice of the matrix to the output stream.
394 * Elements in all rows and in the given range of columns are written.
395 * <P>
396 * <I>Note:</I> <code>theColRange</code>'s stride must be 1.
397 *
398 * @param theColRange Range of matrix columns.
399 *
400 * @exception NullPointerException (unchecked exception) Thrown if
401 * <code>theColRange</code> is null.
402 * @exception IllegalArgumentException (unchecked exception) Thrown if
403 * <code>theColRange</code>'s stride is greater than 1.
404 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
405 * any index in <code>theColRange</code>
406 * is outside the range 0 .. <I>C</I>-1.
407 * @exception IOException Thrown if an I/O error occurred.
408 */
409 public void writeColSlice(Range theColRange)
410 throws IOException {
411 if (theColRange.stride() != 1) {
412 throw new IllegalArgumentException("DoubleMatrixImage.Writer.writeColSlice(): theColRange stride > 1");
413 }
414 int CL = theColRange.lb();
415 int CU = theColRange.ub();
416 if (0 > CL || CL + theColRange.length() > C) {
417 throw new IndexOutOfBoundsException("DoubleMatrixImage.Writer.writeColSlice(): theColRange = "
418 + theColRange + " out of bounds");
419 }
420 write(0, R - 1, CL, CU);
421 }
422
423 /**
424 * Write the given patch of the matrix to the output stream. Elements in
425 * the given range of rows and in the given range of columns are
426 * written.
427 * <P>
428 * <I>Note:</I> <code>theRowRange</code>'s stride must be 1.
429 * <code>theColRange</code>'s stride must be 1.
430 *
431 * @param theRowRange Range of matrix rows.
432 * @param theColRange Range of matrix columns.
433 *
434 * @exception NullPointerException (unchecked exception) Thrown if
435 * <code>theRowRange</code> is null. Thrown if <code>theColRange</code> is null.
436 * @exception IllegalArgumentException (unchecked exception) Thrown if
437 * <code>theRowRange</code>'s stride is greater than 1. Thrown if
438 * <code>theColRange</code>'s stride is greater than 1.
439 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
440 * any index in <code>theRowRange</code>
441 * is outside the range 0 .. <I>R</I>-1. Thrown if any index in
442 * <code>theColRange</code> is outside the range 0 .. <I>C</I>-1.
443 * @exception IOException Thrown if an I/O error occurred.
444 */
445 public void writePatch(Range theRowRange,
446 Range theColRange)
447 throws IOException {
448 if (theRowRange.stride() != 1) {
449 throw new IllegalArgumentException("DoubleMatrixImage.Writer.writePatch(): theRowRange stride > 1");
450 }
451 if (theColRange.stride() != 1) {
452 throw new IllegalArgumentException("DoubleMatrixImage.Writer.writePatch(): theColRange stride > 1");
453 }
454 int RL = theRowRange.lb();
455 int RU = theRowRange.ub();
456 if (0 > RL || RL + theRowRange.length() > R) {
457 throw new IndexOutOfBoundsException("DoubleMatrixImage.Writer.writePatch(): theRowRange = "
458 + theRowRange + " out of bounds");
459 }
460 int CL = theColRange.lb();
461 int CU = theColRange.ub();
462 if (0 > CL || CL + theColRange.length() > C) {
463 throw new IndexOutOfBoundsException("DoubleMatrixImage.Writer.writePatch(): theColRange = "
464 + theColRange + " out of bounds");
465 }
466 write(RL, RU, CL, CU);
467 }
468
469 /**
470 * Close the output stream.
471 *
472 * @exception IOException Thrown if an I/O error occurred.
473 */
474 public void close()
475 throws IOException {
476 myDos.close();
477 }
478
479 // Hidden operations.
480 /**
481 * Write the given rows and columns of the matrix to the output stream.
482 *
483 * @param RL Segment lower row index.
484 * @param RU Segment upper row index.
485 * @param CL Segment lower column index.
486 * @param CU Segment upper column index.
487 *
488 * @exception IOException Thrown if an I/O error occurred.
489 */
490 private void write(int RL,
491 int RU,
492 int CL,
493 int CU)
494 throws IOException {
495 myDos.writeInt(RL); // Segment lower row index
496 myDos.writeInt(CL); // Segment lower column index
497 myDos.writeInt(RU - RL + 1); // Number of rows in segment
498 myDos.writeInt(CU - CL + 1); // Number of columns in segment
499 for (int r = RL; r <= RU; ++r) {
500 double[] myMatrix_r = myMatrix[r];
501 for (int c = CL; c <= CU; ++c) {
502 myDos.writeDouble(myMatrix_r[c]);
503 }
504 }
505 }
506 }
507
508 /**
509 * Class DoubleMatrixFile.Reader provides an object with which to read a
510 * {@linkplain DoubleMatrixFile} from an input stream.
511 * <P>
512 * When a reader is created, the number of rows and number of columns are
513 * read from the input stream. If the matrix file object is uninitialized,
514 * it becomes initialized with the given number of rows and columns, and
515 * storage is allocated for the underlying matrix's row references. If the
516 * matrix file object is already initialized, the number of rows and columns
517 * read from the input stream must match those of the matrix file object,
518 * otherwise an exception is thrown.
519 * <P>
520 * To read the matrix element segments one at a time, first call the
521 * <code>getRowRange()</code> and <code>getColRange()</code> methods to obtain the
522 * range of rows and columns in the next segment. At this point, allocate
523 * storage for the rows and columns in the underlying matrix if necessary.
524 * Then call the <code>readSegment()</code> method to read the actual matrix
525 * elements. Repeat these steps if there are additional segments.
526 * <P>
527 * To read all the matrix element segments (or all the remaining segments),
528 * call the <code>read()</code> method.
529 * <P>
530 * Methods are also provided to read the current segment, or all the
531 * remaining segments, through a <I>filter.</I> As the segment(s) are read,
532 * only those matrix elements within a given row range, a given column
533 * range, or a given row and column range are actually stored in the
534 * underlying matrix.
535 * <P>
536 * When finished, call the <code>close()</code> method.
537 * <P>
538 * <I>Note:</I> Class DoubleMatrixFile.Reader is not multiple thread safe.
539 *
540 * @author Alan Kaminsky
541 * @version 07-Jan-2008
542 */
543 public class Reader {
544
545 // Hidden data members.
546 private InputStream myIs;
547 private DataInputStream myDis;
548 private Range myRowRange;
549 private Range myColRange;
550
551 // Hidden constructors.
552 /**
553 * Construct a new double matrix file reader.
554 *
555 * @param theStream Input stream.
556 *
557 * @exception NullPointerException (unchecked exception) Thrown if
558 * <code>theStream</code> is null.
559 * @exception IOException Thrown if an I/O error occurred.
560 * @exception InvalidMatrixFileException (subclass of IOException)
561 * Thrown if the input stream's contents were invalid.
562 */
563 private Reader(InputStream theStream)
564 throws IOException {
565 if (theStream == null) {
566 throw new NullPointerException("DoubleMatrixFile.Reader(): theStream is null");
567 }
568 myIs = theStream;
569 myDis = new DataInputStream(theStream);
570
571 int R = myDis.readInt(); // Number of matrix rows
572 int C = myDis.readInt(); // Number of matrix columns
573 if (myMatrix == null) {
574 setRC(R, C);
575 myMatrix = new double[R][];
576 } else if (DoubleMatrixFile.this.R != R) {
577 throw new InvalidMatrixFileException("DoubleMatrixFile.Reader(): Number of rows from stream ("
578 + R + ") != number of rows in this matrix file ("
579 + DoubleMatrixFile.this.R + ")");
580 } else if (DoubleMatrixFile.this.C != C) {
581 throw new InvalidMatrixFileException("DoubleMatrixFile.Reader(): Number of columns from stream ("
582 + C + ") != number of columns in this matrix file ("
583 + DoubleMatrixFile.this.C + ")");
584 }
585
586 getNextSegment();
587 }
588
589 // Exported operations.
590 /**
591 * Read all matrix element segments from the input stream. If some
592 * segments have already been read, the <code>read()</code> method reads all
593 * remaining segments. If there are no more segments, the
594 * <code>read()</code> method does nothing. If storage is not already
595 * allocated in the underlying matrix for the matrix elements, the
596 * <code>read()</code> method allocates the necessary storage.
597 *
598 * @exception IOException Thrown if an I/O error occurred.
599 * @exception InvalidMatrixFileException (subclass of IOException)
600 * Thrown if the input stream's contents were invalid.
601 */
602 public void read()
603 throws IOException {
604 while (myRowRange != null) {
605 readSegment();
606 }
607 }
608
609 /**
610 * Read all matrix element segments from the input stream, storing only
611 * the matrix elements in the given row slice. If some segments have
612 * already been read, the <code>readRowSlice()</code> method reads all
613 * remaining segments. If there are no more segments, the
614 * <code>readRowSlice()</code> method does nothing. If storage is not
615 * already allocated in the underlying matrix for the matrix elements,
616 * the <code>readRowSlice()</code> method allocates the necessary storage.
617 *
618 * @param theRowRange Row range.
619 *
620 * @exception NullPointerException (unchecked exception) Thrown if
621 * <code>theRowRange</code> is null.
622 * @exception IllegalArgumentException (unchecked exception) Thrown if
623 * <code>theRowRange</code>'s stride is greater than 1.
624 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
625 * any index in <code>theRowRange</code>
626 * is outside the range 0 .. <I>R</I>-1.
627 * @exception IOException Thrown if an I/O error occurred.
628 * @exception InvalidMatrixFileException (subclass of IOException)
629 * Thrown if the input stream's contents were invalid.
630 */
631 public void readRowSlice(Range theRowRange)
632 throws IOException {
633 while (myRowRange != null) {
634 readSegmentRowSlice(theRowRange);
635 }
636 }
637
638 /**
639 * Read all matrix element segments from the input stream, storing only
640 * the matrix elements in the given column slice. If some segments have
641 * already been read, the <code>readColSlice()</code> method reads all
642 * remaining segments. If there are no more segments, the
643 * <code>readColSlice()</code> method does nothing. If storage is not
644 * already allocated in the underlying matrix for the matrix elements,
645 * the <code>readColSlice()</code> method allocates the necessary storage.
646 *
647 * @param theColRange Column range.
648 *
649 * @exception NullPointerException (unchecked exception) Thrown if
650 * <code>theColRange</code> is null.
651 * @exception IllegalArgumentException (unchecked exception) Thrown if
652 * <code>theColRange</code>'s stride is greater than 1.
653 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
654 * any index in <code>theColRange</code>
655 * is outside the range 0 .. <I>C</I>-1.
656 * @exception IOException Thrown if an I/O error occurred.
657 * @exception InvalidMatrixFileException (subclass of IOException)
658 * Thrown if the input stream's contents were invalid.
659 */
660 public void readColSlice(Range theColRange)
661 throws IOException {
662 while (myRowRange != null) {
663 readSegmentColSlice(theColRange);
664 }
665 }
666
667 /**
668 * Read all matrix element segments from the input stream, storing only
669 * the matrix elements in the given patch. If some segments have already
670 * been read, the <code>readPatch()</code> method reads all remaining
671 * segments. If there are no more segments, the <code>readPatch()</code>
672 * method does nothing. If storage is not already allocated in the
673 * underlying matrix for the matrix elements, the <code>readPatch()</code>
674 * method allocates the necessary storage.
675 *
676 * @param theRowRange Row range.
677 * @param theColRange Column range.
678 *
679 * @exception NullPointerException (unchecked exception) Thrown if
680 * <code>theRowRange</code> is null. Thrown if <code>theColRange</code> is null.
681 * @exception IllegalArgumentException (unchecked exception) Thrown if
682 * <code>theRowRange</code>'s stride is greater than 1. Thrown if
683 * <code>theColRange</code>'s stride is greater than 1.
684 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
685 * any index in <code>theRowRange</code>
686 * is outside the range 0 .. <I>R</I>-1. Thrown if any index in
687 * <code>theColRange</code> is outside the range 0 .. <I>C</I>-1.
688 * @exception IOException Thrown if an I/O error occurred.
689 * @exception InvalidMatrixFileException (subclass of IOException)
690 * Thrown if the input stream's contents were invalid.
691 */
692 public void readPatch(Range theRowRange,
693 Range theColRange)
694 throws IOException {
695 while (myRowRange != null) {
696 readSegmentPatch(theRowRange, theColRange);
697 }
698 }
699
700 /**
701 * Obtain the row range of the next matrix element segment in the input
702 * stream. If there are no more segments, null is returned.
703 *
704 * @return Row range, or null.
705 */
706 public Range getRowRange() {
707 return myRowRange;
708 }
709
710 /**
711 * Obtain the column range of the next matrix element segment in the
712 * input stream. If there are no more segments, null is returned.
713 *
714 * @return Column range, or null.
715 */
716 public Range getColRange() {
717 return myColRange;
718 }
719
720 /**
721 * Read the next matrix element segment from the input stream. If there
722 * are no more segments, the <code>readSegment()</code> method does nothing.
723 * If storage is not already allocated in the underlying matrix for the
724 * matrix elements, the <code>readSegment()</code> method allocates the
725 * necessary storage.
726 *
727 * @exception IOException Thrown if an I/O error occurred.
728 * @exception InvalidMatrixFileException (subclass of IOException)
729 * Thrown if the input stream's contents were invalid.
730 */
731 public void readSegment()
732 throws IOException {
733 readSegment(0, R - 1, 0, C - 1);
734 }
735
736 /**
737 * Read the next matrix element segment from the input stream, storing
738 * only the matrix elements in the given row slice. If there are no more
739 * segments, the <code>readSegmentRowSlice()</code> method does nothing. If
740 * storage is not already allocated in the underlying matrix for the
741 * matrix elements, the <code>readSegmentRowSlice()</code> method allocates
742 * the necessary storage.
743 *
744 * @param theRowRange Row range.
745 *
746 * @exception NullPointerException (unchecked exception) Thrown if
747 * <code>theRowRange</code> is null.
748 * @exception IllegalArgumentException (unchecked exception) Thrown if
749 * <code>theRowRange</code>'s stride is greater than 1.
750 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
751 * any index in <code>theRowRange</code>
752 * is outside the range 0 .. <I>R</I>-1.
753 * @exception IOException Thrown if an I/O error occurred.
754 * @exception InvalidMatrixFileException (subclass of IOException)
755 * Thrown if the input stream's contents were invalid.
756 */
757 public void readSegmentRowSlice(Range theRowRange)
758 throws IOException {
759 if (theRowRange.stride() != 1) {
760 throw new IllegalArgumentException("DoubleMatrixImage.Reader.readSegmentRowSlice(): theRowRange stride > 1");
761 }
762 int RL = theRowRange.lb();
763 int RU = theRowRange.ub();
764 if (0 > RL || RL + theRowRange.length() > R) {
765 throw new IndexOutOfBoundsException("DoubleMatrixImage.Reader.readSegmentRowSlice(): theRowRange = "
766 + theRowRange + " out of bounds");
767 }
768 readSegment(RL, RU, 0, C - 1);
769 }
770
771 /**
772 * Read the next matrix element segment from the input stream, storing
773 * only the matrix elements in the given column slice. If there are no
774 * more segments, the <code>readSegmentRowSlice()</code> method does
775 * nothing. If storage is not already allocated in the underlying matrix
776 * for the matrix elements, the <code>readSegmentRowSlice()</code> method
777 * allocates the necessary storage.
778 *
779 * @param theColRange Column range.
780 *
781 * @exception NullPointerException (unchecked exception) Thrown if
782 * <code>theColRange</code> is null.
783 * @exception IllegalArgumentException (unchecked exception) Thrown if
784 * <code>theColRange</code>'s stride is greater than 1.
785 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
786 * any index in <code>theColRange</code>
787 * is outside the range 0 .. <I>C</I>-1.
788 * @exception IOException Thrown if an I/O error occurred.
789 * @exception InvalidMatrixFileException (subclass of IOException)
790 * Thrown if the input stream's contents were invalid.
791 */
792 public void readSegmentColSlice(Range theColRange)
793 throws IOException {
794 if (theColRange.stride() != 1) {
795 throw new IllegalArgumentException("DoubleMatrixImage.Reader.readSegmentColSlice(): theColRange stride > 1");
796 }
797 int CL = theColRange.lb();
798 int CU = theColRange.ub();
799 if (0 > CL || CL + theColRange.length() > C) {
800 throw new IndexOutOfBoundsException("DoubleMatrixImage.Reader.readSegmentColSlice(): theColRange = "
801 + theColRange + " out of bounds");
802 }
803 readSegment(0, R - 1, CL, CU);
804 }
805
806 /**
807 * Read the next matrix element segment from the input stream, storing
808 * only the matrix elements in the given patch. If there are no more
809 * segments, the <code>readSegmentPatch()</code> method does nothing. If
810 * storage is not already allocated in the underlying matrix for the
811 * matrix elements, the <code>readSegmentPatch()</code> method allocates the
812 * necessary storage.
813 *
814 * @param theRowRange Row range.
815 * @param theColRange Column range.
816 *
817 * @exception NullPointerException (unchecked exception) Thrown if
818 * <code>theRowRange</code> is null. Thrown if <code>theColRange</code> is null.
819 * @exception IllegalArgumentException (unchecked exception) Thrown if
820 * <code>theRowRange</code>'s stride is greater than 1. Thrown if
821 * <code>theColRange</code>'s stride is greater than 1.
822 * @exception IndexOutOfBoundsException (unchecked exception) Thrown if
823 * any index in <code>theRowRange</code>
824 * is outside the range 0 .. <I>R</I>-1. Thrown if any index in
825 * <code>theColRange</code> is outside the range 0 .. <I>C</I>-1.
826 * @exception IOException Thrown if an I/O error occurred.
827 * @exception InvalidMatrixFileException (subclass of IOException)
828 * Thrown if the input stream's contents were invalid.
829 */
830 public void readSegmentPatch(Range theRowRange,
831 Range theColRange)
832 throws IOException {
833 if (theRowRange.stride() != 1) {
834 throw new IllegalArgumentException("DoubleMatrixImage.Reader.readSegmentPatch(): theRowRange stride > 1");
835 }
836 int RL = theRowRange.lb();
837 int RU = theRowRange.ub();
838 if (0 > RL || RL + theRowRange.length() > R) {
839 throw new IndexOutOfBoundsException("DoubleMatrixImage.Reader.readSegmentPatch(): theRowRange = "
840 + theRowRange + " out of bounds");
841 }
842 if (theColRange.stride() != 1) {
843 throw new IllegalArgumentException("DoubleMatrixImage.Reader.readSegmentPatch(): theColRange stride > 1");
844 }
845 int CL = theColRange.lb();
846 int CU = theColRange.ub();
847 if (0 > CL || CL + theColRange.length() > C) {
848 throw new IndexOutOfBoundsException("DoubleMatrixImage.Reader.readSegmentPatch(): theColRange = "
849 + theColRange + " out of bounds");
850 }
851 readSegment(RL, RU, CL, CU);
852 }
853
854 /**
855 * Close the input stream.
856 *
857 * @exception IOException Thrown if an I/O error occurred.
858 */
859 public void close()
860 throws IOException {
861 myDis.close();
862 }
863
864 // Hidden operations.
865 /**
866 * Read the bounds of the next segment from the input stream and store
867 * them in myRowRange and myColRange.
868 *
869 * @exception IOException Thrown if an I/O error occurred.
870 * @exception InvalidMatrixFileException (subclass of IOException)
871 * Thrown if the input stream's contents were invalid.
872 */
873 private void getNextSegment()
874 throws IOException {
875 try {
876 int RL = myDis.readInt(); // Segment lower row index
877 int CL = myDis.readInt(); // Segment lower column index
878 int M = myDis.readInt(); // Number of rows in segment
879 int N = myDis.readInt(); // Number of columns in segment
880
881 if (RL < 0) {
882 throw new InvalidMatrixFileException("DoubleMatrixFile.Reader.getNextSegment(): Invalid segment lower row index ("
883 + RL + ")");
884 }
885 if (CL < 0) {
886 throw new InvalidMatrixFileException("DoubleMatrixFile.Reader.getNextSegment(): Invalid segment lower column index ("
887 + CL + ")");
888 }
889 if (M < 0 || RL + M > R) {
890 throw new InvalidMatrixFileException("DoubleMatrixFile.Reader.getNextSegment(): Invalid numer of rows in segment ("
891 + M + ")");
892 }
893 if (N < 0 || CL + N > C) {
894 throw new InvalidMatrixFileException("DoubleMatrixFile.Reader.getNextSegment(): Invalid numer of columns in segment ("
895 + N + ")");
896 }
897
898 myRowRange = new Range(RL, RL + M - 1);
899 myColRange = new Range(CL, CL + N - 1);
900 } catch (EOFException exc) {
901 myRowRange = null;
902 myColRange = null;
903 }
904 }
905
906 /**
907 * Read the next matrix element segment from the input stream, storing
908 * only the matrix elements within the given row and column index
909 * bounds. If there are no more segments, the <code>readSegment()</code>
910 * method does nothing. If storage is not already allocated in the
911 * underlying matrix for the matrix elements, the <code>readSegment()</code>
912 * method allocates the necessary storage.
913 *
914 * @param RL Lower row index.
915 * @param RU Upper row index.
916 * @param CL Lower column index.
917 * @param CU Upper column index.
918 *
919 * @exception IOException Thrown if an I/O error occurred.
920 * @exception InvalidMatrixFileException (subclass of IOException)
921 * Thrown if the input stream's contents were invalid.
922 */
923 private void readSegment(int RL,
924 int RU,
925 int CL,
926 int CU)
927 throws IOException {
928 // Early return if no more segments.
929 if (myRowRange == null) {
930 return;
931 }
932
933 // Get row and column bounds of segment.
934 int SRL = myRowRange.lb();
935 int SRU = myRowRange.ub();
936 int SCL = myColRange.lb();
937 int SCU = myColRange.ub();
938
939 // Number of bytes in an entire row.
940 long rowBytes = BYTES_PER_ELEMENT * myColRange.length();
941
942 // First row to read.
943 int firstRow = Math.max(RL, SRL);
944
945 // Last row to read.
946 int lastRow = Math.min(RU, SRU);
947
948 // Number of rows (bytes) to skip at the beginning of the segment.
949 int preSkipRows = firstRow - SRL;
950 long preSkipRowBytes = preSkipRows * rowBytes;
951
952 // Number of rows (bytes) to skip at the end of the segment.
953 int postSkipRows = SRU - lastRow;
954 long postSkipRowBytes = postSkipRows * rowBytes;
955
956 // First column to read.
957 int firstCol = Math.max(CL, SCL);
958
959 // Last column to read.
960 int lastCol = Math.min(CU, SCU);
961
962 // Number of columns (bytes) to skip at the beginning of the row.
963 int preSkipCols = firstCol - SCL;
964 long preSkipColBytes = preSkipCols * BYTES_PER_ELEMENT;
965
966 // Number of columns (bytes) to skip at the end of the row.
967 int postSkipCols = SCU - lastCol;
968 long postSkipColBytes = postSkipCols * BYTES_PER_ELEMENT;
969
970 // Skip rows at beginning of segment.
971 skipFully(preSkipRowBytes);
972
973 // Read rows.
974 for (int r = firstRow; r <= lastRow; ++r) {
975 // Allocate storage for row if necessary.
976 double[] myMatrix_r = myMatrix[r];
977 if (myMatrix_r == null) {
978 myMatrix_r = new double[C];
979 myMatrix[r] = myMatrix_r;
980 }
981
982 // Skip columns at beginning of row.
983 skipFully(preSkipColBytes);
984
985 // Read columns.
986 for (int c = firstCol; c <= lastCol; ++c) {
987 myMatrix_r[c] = myDis.readDouble();
988 }
989
990 // Skip columns at end of row.
991 skipFully(postSkipColBytes);
992 }
993
994 // Skip rows at end of segment.
995 skipFully(postSkipRowBytes);
996
997 getNextSegment();
998 }
999
1000 /**
1001 * Skip over the given number of bytes in the input stream.
1002 *
1003 * @param n Number of bytes to skip.
1004 *
1005 * @exception IOException Thrown if an I/O error occurred.
1006 */
1007 private void skipFully(long n)
1008 throws IOException {
1009 while (n > 0L) {
1010 n -= myDis.skip(n);
1011 }
1012 }
1013
1014 }
1015
1016 // Hidden operations.
1017 /**
1018 * Set this matrix file's number of rows and number of columns.
1019 *
1020 * @param R Number of rows.
1021 * @param C Number of columns.
1022 *
1023 * @exception IllegalArgumentException (unchecked exception) Thrown if
1024 * <code>R</code> < 0. Thrown if
1025 * <code>C</code> < 0.
1026 */
1027 void setRC(int R,
1028 int C) {
1029 if (R < 0) {
1030 throw new IllegalArgumentException("DoubleMatrixFile.setHeightAndWidth(): R = " + R + " illegal");
1031 }
1032 if (C < 0) {
1033 throw new IllegalArgumentException("DoubleMatrixFile.setHeightAndWidth(): C = " + C + " illegal");
1034 }
1035 this.R = R;
1036 this.C = C;
1037 }
1038
1039 }