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 }