001/* A token that contains a 2-D boolean matrix.
002
003 Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 There are currently no interesting operations implemented.
028 */
029package ptolemy.data;
030
031import ptolemy.data.expr.ASTPtRootNode;
032import ptolemy.data.expr.ParseTreeEvaluator;
033import ptolemy.data.expr.PtParser;
034import ptolemy.data.type.BaseType;
035import ptolemy.data.type.Type;
036import ptolemy.data.type.TypeLattice;
037import ptolemy.graph.CPO;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040
041///////////////////////////////////////////////////////////////////
042//// BooleanMatrixToken
043
044/**
045 A token that contains a 2-D boolean matrix.
046
047 @author Yuhong Xiong, Steve Neuendorffer
048 @version $Id$
049 @since Ptolemy II 0.2
050 @Pt.ProposedRating Green (neuendor)
051 @Pt.AcceptedRating Yellow (cxh)
052 */
053public class BooleanMatrixToken extends MatrixToken {
054    /** Construct an BooleanMatrixToken with a one by one matrix. The
055     *  only element in the matrix has value false.
056     */
057    public BooleanMatrixToken() {
058        _rowCount = 1;
059        _columnCount = 1;
060        _value = new boolean[1][1];
061        _value[0][0] = false;
062    }
063
064    /** Construct a BooleanMatrixToken with the specified 2-D matrix.
065     *  This method makes a copy of the matrix and stores the copy,
066     *  so changes on the specified matrix after this token is
067     *  constructed will not affect the content of this token.
068     *  @param value The 2-D boolean matrix.
069     *  @exception IllegalActionException If the specified matrix
070     *   is null.
071     */
072    public BooleanMatrixToken(boolean[][] value) throws IllegalActionException {
073        if (value == null) {
074            throw new IllegalActionException(
075                    "BooleanMatrixToken: The " + "specified matrix is null.");
076        }
077
078        _initialize(value);
079    }
080
081    /** Construct a BooleanMatrixToken from the specified string.
082     *  @param init A string expression of a boolean matrix.
083     *  @exception IllegalActionException If the string does
084     *   not contain a parsable boolean matrix.
085     */
086    public BooleanMatrixToken(String init) throws IllegalActionException {
087        PtParser parser = new PtParser();
088        ASTPtRootNode tree = parser.generateParseTree(init);
089        Token token = new ParseTreeEvaluator().evaluateParseTree(tree);
090
091        if (token instanceof BooleanMatrixToken) {
092            boolean[][] value = ((BooleanMatrixToken) token).booleanMatrix();
093            _initialize(value);
094        } else {
095            throw new IllegalActionException("A BooleanMatrixToken cannot be"
096                    + " created from the expression '" + init + "'");
097        }
098    }
099
100    /** Construct an BooleanMatrixToken from the specified array of
101     *  tokens.  The tokens in the array must be scalar tokens
102     *  convertible into integers.
103     *  @param tokens The array of tokens, which must contains
104     *  rows*columns BooleanTokens.
105     *  @param rows The number of rows in the matrix to be created.
106     *  @param columns The number of columns in the matrix to be
107     *  created.
108     *  @exception IllegalActionException If the array of tokens is
109     *  null, or the length of the array is not correct, or if one of
110     *  the elements of the array is null, or if one of the elements
111     *  of the array cannot be losslessly converted to a boolean.
112     */
113    public BooleanMatrixToken(Token[] tokens, int rows, int columns)
114            throws IllegalActionException {
115        if (tokens == null) {
116            throw new IllegalActionException(
117                    "BooleanMatrixToken: The specified" + " array is null.");
118        }
119
120        if (tokens.length != rows * columns) {
121            throw new IllegalActionException("BooleanMatrixToken: The specified"
122                    + " array is not of the correct length");
123        }
124
125        _rowCount = rows;
126        _columnCount = columns;
127        _value = new boolean[rows][columns];
128
129        for (int i = 0; i < tokens.length; i++) {
130            Token token = tokens[i];
131
132            if (token instanceof BooleanToken) {
133                _value[i / columns][i % columns] = ((BooleanToken) token)
134                        .booleanValue();
135            } else {
136                throw new IllegalActionException("BooleanMatrixToken: Element "
137                        + i + " in the array with value " + token
138                        + " is not a ScalarToken");
139            }
140        }
141    }
142
143    ///////////////////////////////////////////////////////////////////
144    ////                         public methods                    ////
145
146    /** Return a copy of the contained 2-D matrix.
147     *  It is safe for the caller to modify the returned matrix.
148     *  @return A 2-D boolean matrix.
149     */
150    @Override
151    public boolean[][] booleanMatrix() {
152        boolean[][] result = new boolean[_rowCount][_columnCount];
153
154        for (int i = 0; i < _rowCount; i++) {
155            for (int j = 0; j < _columnCount; j++) {
156                result[i][j] = _value[i][j];
157            }
158        }
159
160        return result;
161    }
162
163    /** Convert the specified token into an instance of BooleanMatrixToken.
164     *  This method does lossless conversion.
165     *  If the argument is already an instance of BooleanMatrixToken,
166     *  it is returned without any change. Otherwise, if the argument
167     *  is below BooleanMatrixToken in the type hierarchy, it is converted to
168     *  an instance of BooleanMatrixToken or one of the subclasses of
169     *  BooleanMatrixToken and returned. If none of the above conditions are
170     *  met, an exception is thrown.
171     *  @param token The token to be converted to a BooleanMatrixToken.
172     *  @return A BooleanMatrixToken
173     *  @exception IllegalActionException If the conversion cannot
174     *   be carried out.
175     */
176    public static BooleanMatrixToken convert(Token token)
177            throws IllegalActionException {
178        if (token instanceof BooleanMatrixToken) {
179            return (BooleanMatrixToken) token;
180        }
181
182        int compare = TypeLattice.compare(BaseType.BOOLEAN_MATRIX, token);
183
184        if (compare == CPO.LOWER || compare == CPO.INCOMPARABLE) {
185            throw new IllegalActionException(
186                    notSupportedIncomparableConversionMessage(token,
187                            "[boolean]"));
188        }
189
190        // try boolean
191        //         compare = TypeLattice.compare(BaseType.BOOLEAN, token);
192        //         if (compare == CPO.SAME || compare == CPO.HIGHER) {
193        //             BooleanToken tem = (BooleanToken)
194        //                 BooleanToken.convert(token);
195        //             boolean[][] result = new boolean[1][1];
196        //             result[0][0] = tem.booleanValue();
197        //             return new BooleanMatrixToken(result);
198        //         }
199        // The argument is below BooleanMatrixToken in the type hierarchy,
200        // but I don't recognize it.
201        throw new IllegalActionException(
202                notSupportedConversionMessage(token, "[boolean]"));
203    }
204
205    /** Return a new matrix that is a sub-matrix of this matrix.
206     *  @param rowStart The row to start on.
207     *  @param colStart The column to start on.
208     *  @param rowSpan The number of rows to copy.
209     *  @param colSpan The number of columns to copy.
210     *  @return a sub-matrix of this matrix.
211     *  @exception IllegalActionException If the returned matrix is empty or if the specified
212     *   parameters result in out of bounds accesses.
213     */
214    @Override
215    public MatrixToken crop(int rowStart, int colStart, int rowSpan,
216            int colSpan) throws IllegalActionException {
217        boolean[][] value = this.booleanMatrix();
218        try {
219            boolean[][] result = new boolean[rowSpan][colSpan];
220            for (int i = 0; i < rowSpan; i++) {
221                System.arraycopy(value[rowStart + i], colStart, result[i], 0,
222                        colSpan);
223            }
224            return new BooleanMatrixToken(result);
225        } catch (ArrayIndexOutOfBoundsException ex) {
226            throw new IllegalActionException(
227                    "Matrix crop indices out of bounds (rowStart = " + rowStart
228                            + ", colStart = " + colStart + ", rowSpan = "
229                            + rowSpan + ", colSpan = " + colSpan + ").");
230        }
231    }
232
233    /** Return true if the argument is an instance of BooleanMatrixToken
234     *  of the same dimensions and the corresponding elements of the matrices
235     *  are equal.
236     *  @param object An instance of Object.
237     *  @return True if the argument is an instance of BooleanMatrixToken
238     *   of the same dimensions and the corresponding elements of the
239     *   matrices are equal.
240     */
241    @Override
242    public boolean equals(Object object) {
243        if (object == null) {
244            return false;
245        }
246        // This test rules out instances of a subclass.
247        if (object.getClass() != getClass()) {
248            return false;
249        }
250
251        BooleanMatrixToken matrixArgument = (BooleanMatrixToken) object;
252
253        if (_rowCount != matrixArgument.getRowCount()) {
254            return false;
255        }
256
257        if (_columnCount != matrixArgument.getColumnCount()) {
258            return false;
259        }
260
261        boolean[][] matrix = matrixArgument.booleanMatrix();
262
263        for (int i = 0; i < _rowCount; i++) {
264            for (int j = 0; j < _columnCount; j++) {
265                if (_value[i][j] != matrix[i][j]) {
266                    return false;
267                }
268            }
269        }
270
271        return true;
272    }
273
274    /** Return the number of columns in the matrix.
275     *  @return The number of columns in the matrix.
276     */
277    @Override
278    public int getColumnCount() {
279        return _columnCount;
280    }
281
282    /** Return the element of the matrix at the specified
283     *  row and column in a BooleanToken.
284     *  @param row The row index of the desired element.
285     *  @param column The column index of the desired element.
286     *  @return A BooleanToken containing the matrix element.
287     *  @exception ArrayIndexOutOfBoundsException If the specified
288     *   row or column number is outside the range of the matrix.
289     */
290    @Override
291    public Token getElementAsToken(int row, int column)
292            throws ArrayIndexOutOfBoundsException {
293        return BooleanToken.getInstance(_value[row][column]);
294    }
295
296    /** Return the element of the contained matrix at the specified
297     *  row and column.
298     *  @param row The row index of the desired element.
299     *  @param column The column index of the desired element.
300     *  @return The boolean at the specified matrix entry.
301     *  @exception ArrayIndexOutOfBoundsException If the specified
302     *   row or column number is outside the range of the matrix.
303     */
304    public boolean getElementAt(int row, int column) {
305        return _value[row][column];
306    }
307
308    /** Return the Type of the tokens contained in this matrix token.
309     *  @return BaseType.INT.
310     */
311    @Override
312    public Type getElementType() {
313        return BaseType.INT;
314    }
315
316    /** Return the number of rows in the matrix.
317     *  @return The number of rows in the matrix.
318     */
319    @Override
320    public int getRowCount() {
321        return _rowCount;
322    }
323
324    /** Return the type of this token.
325     *  @return BaseType.BOOLEAN_MATRIX
326     */
327    @Override
328    public Type getType() {
329        return BaseType.BOOLEAN_MATRIX;
330    }
331
332    /** Return a hash code value for this token. This method returns the
333     *  number of elements with value true in the contained matrix.
334     *  @return A hash code value for this token.
335     */
336    @Override
337    public int hashCode() {
338        int code = 0;
339
340        for (int i = 0; i < _rowCount; i++) {
341            for (int j = 0; j < _columnCount; j++) {
342                if (_value[i][j]) {
343                    code++;
344                }
345            }
346        }
347
348        return code;
349    }
350
351    /** Join a matrix of matrices into a single matrix by tiling.
352     *  All matrices in the matrix must be of the same type,
353     *  the same type as this matrix. But none of them needs to
354     *  actually be this matrix. This base class simply throws
355     *  an exception. Derived classes provide the implementation.
356     *  The number of columns in the resulting matrix is the sum
357     *  of the number of columns in the first row of the argument.
358     *  The number of rows in the resulting matrix is the sum
359     *  of the number of rows in the first column of the argument.
360     *  The matrices are copied into the result starting at the
361     *  position determined by the first row or column.
362     *  If the matrices overlap, then while copying left to right,
363     *  top-to-bottom, data will be overwritten. If there are gaps,
364     *  the resulting matrix will be filled with zeros.
365     *  @param matrices A two-dimensional array of matrix tokens.
366     *  @return A new matrix token of the same type as the elements
367     *   in the input matrix of matrix tokens.
368     *  @exception IllegalActionException If the types of the matrices
369     *   in the input are not all the same, or if tiling fails due
370     *   to size incompatibilities, or if the input matrix has no
371     *   tokens.
372     */
373    @Override
374    public MatrixToken join(MatrixToken[][] matrices)
375            throws IllegalActionException {
376        if (matrices == null || matrices.length == 0
377                || matrices[0].length == 0) {
378            throw new IllegalActionException("matrixJoin: No input matrices.");
379        }
380        // Calculate the size of the result.
381        // This assumes the matrices tile.
382        int rows = 0;
383        int columns = 0;
384        for (MatrixToken[] matrice : matrices) {
385            rows += matrice[0].getRowCount();
386        }
387        for (int j = 0; j < matrices[0].length; j++) {
388            columns += matrices[0][j].getColumnCount();
389        }
390        boolean[][] tiled = new boolean[rows][columns];
391        int row = 0;
392        for (int i = 0; i < matrices.length; i++) {
393            int column = 0;
394            for (int j = 0; j < matrices[i].length; j++) {
395                if (!(matrices[i][j] instanceof BooleanMatrixToken)) {
396                    throw new IllegalActionException(
397                            "matrixJoin: matrices not all of the same type.");
398                }
399                int rowCount = matrices[i][j].getRowCount();
400                if (row + rowCount > rows) {
401                    rowCount = rows - row;
402                }
403                int columnCount = matrices[i][j].getColumnCount();
404                if (column + columnCount > columns) {
405                    columnCount = columns - column;
406                }
407                // There is no BooleanMatrixMath class, so we need
408                // to implement the matrix copy here.
409                for (int ii = 0; ii < rowCount; ii++) {
410                    System.arraycopy(matrices[i][j].booleanMatrix()[ii], 0,
411                            tiled[row + ii], column, columnCount);
412                }
413                // Starting position for the next column.
414                column += matrices[0][j].getColumnCount();
415            }
416            // Starting position for the next column.
417            row += matrices[i][0].getRowCount();
418        }
419        return new BooleanMatrixToken(tiled);
420    }
421
422    /** Split this matrix into multiple matrices. See the base
423     *  class for documentation.
424     *  @param rows The number of rows per submatrix.
425     *  @param columns The number of columns per submatrix.
426     *  @return An array of matrix tokens.
427     */
428    @Override
429    public MatrixToken[][] split(int[] rows, int[] columns) {
430        MatrixToken[][] result = new MatrixToken[rows.length][columns.length];
431        boolean[][] source = booleanMatrix();
432        int row = 0;
433        for (int i = 0; i < rows.length; i++) {
434            int column = 0;
435            for (int j = 0; j < columns.length; j++) {
436                boolean[][] contents = new boolean[rows[i]][columns[j]];
437                int rowspan = rows[i];
438                if (row + rowspan > source.length) {
439                    rowspan = source.length - row;
440                }
441                int columnspan = columns[j];
442                if (column + columnspan > source[0].length) {
443                    columnspan = source[0].length - column;
444                }
445                if (columnspan > 0 && rowspan > 0) {
446                    // There is no BooleanMatrixMath class, so we need
447                    // to implement the matrix copy here.
448                    for (int ii = 0; ii < rowspan; ii++) {
449                        System.arraycopy(source[row + ii], column, contents[ii],
450                                0, columnspan);
451                    }
452                }
453                column += columns[j];
454                try {
455                    result[i][j] = new BooleanMatrixToken(contents);
456                } catch (IllegalActionException e) {
457                    throw new InternalErrorException(e);
458                }
459            }
460            row += rows[i];
461        }
462        return result;
463    }
464
465    /** Return a new Token representing the left multiplicative
466     *  identity. The returned token contains an identity matrix
467     *  whose dimensions are the same as the number of rows of
468     *  the matrix contained in this token.
469     *  @return A new BooleanMatrixToken containing the left multiplicative
470     *   identity.
471     */
472    @Override
473    public Token one() {
474        try {
475            return new BooleanMatrixToken(_createIdentity(_rowCount));
476        } catch (IllegalActionException illegalAction) {
477            // should not happen
478            throw new InternalErrorException("BooleanMatrixToken.one: "
479                    + "Cannot create identity matrix.");
480        }
481    }
482
483    /** Return a new Token representing the right multiplicative
484     *  identity. The returned token contains an identity matrix
485     *  whose dimensions are the same as the number of columns of
486     *  the matrix contained in this token.
487     *  @return A new BooleanMatrixToken containing the right multiplicative
488     *   identity.
489     */
490    @Override
491    public Token oneRight() {
492        try {
493            return new BooleanMatrixToken(_createIdentity(_columnCount));
494        } catch (IllegalActionException illegalAction) {
495            // should not happen
496            throw new InternalErrorException("BooleanMatrixToken.oneRight: "
497                    + "Cannot create identity matrix.");
498        }
499    }
500
501    /** Return a new Token representing the additive identity.
502     *  The returned token contains a matrix whose elements are
503     *  all zero, and the size of the matrix is the same as the
504     *  matrix contained in this token.
505     *  @return A new IntMatrixToken containing the additive identity.
506     */
507    @Override
508    public Token zero() {
509        try {
510            return new BooleanMatrixToken(new boolean[_rowCount][_columnCount]);
511        } catch (IllegalActionException illegalAction) {
512            // should not happen
513            throw new InternalErrorException(
514                    "BooleanMatrixToken.zero: " + "Cannot create zero matrix.");
515        }
516    }
517
518    ///////////////////////////////////////////////////////////////////
519    ////                         protected methods                 ////
520
521    /** Return an new identity matrix with the specified dimension. The
522     *  matrix is square, so only one dimension specifier is needed.
523     *  @param dim The dimension
524     *  @return the identity matrix.
525     */
526    protected boolean[][] _createIdentity(int dim) {
527        boolean[][] a = new boolean[dim][dim];
528
529        // we rely on the fact Java fills the allocated matrix with false.
530        for (int i = 0; i < dim; i++) {
531            a[i][i] = true;
532        }
533
534        return a;
535    }
536
537    ///////////////////////////////////////////////////////////////////
538    ////                         private methods                   ////
539    // Initialize the row and column count and copy the specified
540    // matrix. This method is used by the constructors.
541    private void _initialize(boolean[][] value) {
542        _rowCount = value.length;
543        _columnCount = value[0].length;
544        _value = new boolean[_rowCount][_columnCount];
545
546        for (int i = 0; i < _rowCount; i++) {
547            for (int j = 0; j < _columnCount; j++) {
548                _value[i][j] = value[i][j];
549            }
550        }
551    }
552
553    ///////////////////////////////////////////////////////////////////
554    ////                         private variables                 ////
555    private boolean[][] _value = null;
556
557    private int _rowCount = 0;
558
559    private int _columnCount = 0;
560}