View Javadoc
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> &gt;= 0.
87   * <LI>
88   * Number of matrix columns, <I>C</I> (<code>int</code>). <I>C</I> &gt;= 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> &gt;= 0.
97   * <LI>
98   * The segment's lower column index, <I>CL</I> (<code>int</code>). <I>CL</I> &gt;=
99   * 0.
100  * <LI>
101  * Number of rows in the segment, <I>M</I> (<code>int</code>). <I>M</I> &gt;= 0.
102  * <I>RL</I>+<I>M</I> &lt;= <I>R</I>.
103  * <LI>
104  * Number of columns in the segment, <I>N</I> (<code>int</code>). <I>N</I> &gt;= 0.
105  * <I>CL</I>+<I>N</I> &lt;= <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> &lt; 0. Thrown if
145      * <code>C</code> &lt; 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> &lt; 0. Thrown if
193      * <code>C</code> &lt; 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> &lt; 0. Thrown if
1025      * <code>C</code> &lt; 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 }