001/**
002 * Matrix3D -- A 3x3 matrix of Parameter objects.
003 *
004 * Copyright (C) 2008-2015, by Joseph A. Huwaldt. All rights reserved.
005 *
006 * This library is free software; you can redistribute it and/or modify it under the terms
007 * of the GNU Lesser General Public License as published by the Free Software Foundation;
008 * either version 2 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
012 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License along with
015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html
017 */
018package jahuwaldt.js.param;
019
020import java.text.MessageFormat;
021import java.util.List;
022import java.util.ResourceBundle;
023import javax.measure.converter.UnitConverter;
024import javax.measure.quantity.*;
025import javax.measure.unit.NonSI;
026import javax.measure.unit.SI;
027import javax.measure.unit.Unit;
028import javolution.context.ObjectFactory;
029import javolution.context.StackContext;
030import javolution.lang.MathLib;
031import javolution.lang.Realtime;
032import javolution.lang.ValueType;
033import javolution.util.FastTable;
034import javolution.xml.XMLSerializable;
035import org.jscience.mathematics.number.Float64;
036import org.jscience.mathematics.vector.*;
037
038/**
039 * <p>
040 * This class represents a 3x3 {@link Matrix matrix} implementation for
041 * {@link Parameter parameter} elements.</p>
042 *
043 * <p>
044 * Instances of this class can be created from {@link Vector3D}, either as rows or columns
045 * if the matrix is transposed. For example:
046 * <pre>
047 *         Vector3D<Length> column0 = Vector3D.valueOf(...);
048 *         Vector3D<Length> column1 = Vector3D.valueOf(...);
049 *         Vector3D<Length> column2 = Vector3D.valueOf(...);
050 *         Matrix3D<Length> M = Matrix3D.valueOf(column0, column1, column2).transpose();
051 * </pre></p>
052 *
053 * <p> Modified by: Joseph A. Huwaldt </p>
054 * 
055 * @author Joseph A. Huwaldt, Date: November 26, 2008
056 * @version October 23, 2015
057 */
058public final class Matrix3D<Q extends Quantity> extends Matrix<Parameter<Q>> implements Realtime, ValueType, XMLSerializable {
059
060    private static final long serialVersionUID = -3295634256026052059L;
061
062    /**
063     * The resource bundle for this package.
064     */
065    protected static final ResourceBundle RESOURCES = Parameter.RESOURCES;
066
067    /**
068     * The elements stored in this matrix. Serialization is handled by this class since
069     * Float64Matrix is not Serializable.
070     */
071    private transient Float64Matrix _data;
072
073    /**
074     * Holds the units of the matrix elements.
075     */
076    private Unit<Q> _unit;
077
078    /**
079     * Returns 3D matrix from a sequence of 9 <code>double</code> values.
080     * <p>
081     * Values are ordered as follows:
082     * <pre>
083     *         | a b c |
084     *         | d e f |
085     *         | g h i |
086     * </pre></p>
087     *
088     * @param a    matrix element 0,0
089     * @param b    matrix element 0,1
090     * @param c    matrix element 0,2
091     * @param d    matrix element 1,0
092     * @param e    matrix element 1,1
093     * @param f    matrix element 1,2
094     * @param g    matrix element 2,0
095     * @param h    matrix element 2,1
096     * @param i    matrix element 2,2
097     * @param unit the unit in which the elements are stated.
098     * @return the 3D matrix having the specified elements stated in the specified units.
099     */
100    public static <Q extends Quantity> Matrix3D<Q> valueOf(double a, double b, double c, double d, double e, double f,
101            double g, double h, double i, Unit<Q> unit) {
102        Float64Vector row0 = Float64Vector.valueOf(a, b, c);
103        Float64Vector row1 = Float64Vector.valueOf(d, e, f);
104        Float64Vector row2 = Float64Vector.valueOf(g, h, i);
105
106        Float64Matrix data = Float64Matrix.valueOf(row0, row1, row2);
107
108        Matrix3D<Q> M = Matrix3D.newInstance(unit);
109        M._data = data;
110
111        return M;
112    }
113
114    /**
115     * Returns a 3D matrix holding the specified row vectors in the stated units (column
116     * vectors if {@link #transpose transposed}).
117     *
118     * @param row0 the 1st row vector.
119     * @param row1 the 2nd row vector.
120     * @param row2 the 3rd row vector.
121     * @param unit the unit in which the elements are stated.
122     * @return the 3D matrix having the specified elements stated in the specified units.
123     */
124    public static <Q extends Quantity> Matrix3D<Q> valueOf(Float64Vector row0, Float64Vector row1, Float64Vector row2, Unit<Q> unit) {
125
126        Float64Matrix data = Float64Matrix.valueOf(row0, row1, row2);
127
128        Matrix3D<Q> M = Matrix3D.newInstance(unit);
129        M._data = data;
130
131        return M;
132    }
133
134    /**
135     * Returns a 3D matrix holding the specified row vectors (column vectors if
136     * {@link #transpose transposed}). All the values are converted to the same units as
137     * the 1st row (row0).
138     *
139     * @param row0 the 1st row vector.
140     * @param row1 the 2nd row vector.
141     * @param row2 the 3rd row vector.
142     * @return the 3D matrix having the specified rows.
143     */
144    public static <Q extends Quantity> Matrix3D<Q> valueOf(Vector3D<Q> row0, Vector3D<Q> row1, Vector3D<Q> row2) {
145        Unit<Q> unit = row0.getUnit();
146
147        Float64Matrix data = Float64Matrix.valueOf(row0.toFloat64Vector(),
148                row1.to(unit).toFloat64Vector(), row2.to(unit).toFloat64Vector());
149
150        Matrix3D<Q> M = Matrix3D.newInstance(unit);
151        M._data = data;
152
153        return M;
154    }
155
156    /**
157     * Returns a 3D matrix holding the row {@link Vector3D vectors} from the specified
158     * collection (column vectors if {@link #transpose transposed}). All the values are
159     * converted to the same units as the 1st row (row0).
160     *
161     * @param rows The list of row vectors. If there are more than 3 elements, an
162     *             exception is thrown.
163     * @return the 3D matrix having the specified rows.
164     * @throws DimensionException if the rows do not have a dimension of 3.
165     */
166    public static <Q extends Quantity> Matrix3D<Q> valueOf(List<Vector3D<Q>> rows) {
167        if (rows.size() != 3)
168            throw new DimensionException(
169                    MessageFormat.format(RESOURCES.getString("3vectorsExpectedErr"), rows.size()));
170
171        return Matrix3D.valueOf((Vector3D<Q>)rows.get(0), (Vector3D<Q>)rows.get(1), (Vector3D<Q>)rows.get(2));
172    }
173
174    /**
175     * Returns a {@link Matrix3D} instance containing the specified matrix of Float64
176     * values stated in the specified units. The input matrix must have dimensions of 3x3.
177     *
178     * @param matrix the matrix of Float64 values stated in the specified unit (must have
179     *               dimension of 3x3).
180     * @param unit   the unit in which the elements are stated.
181     * @return the matrix having the specified values.
182     */
183    public static <Q extends Quantity> Matrix3D<Q> valueOf(Matrix<Float64> matrix, Unit<Q> unit) {
184        if (matrix.getNumberOfColumns() != 3 || matrix.getNumberOfRows() != 3)
185            throw new DimensionException(MessageFormat.format(RESOURCES.getString("3x3MatrixExpectedErr"),
186                    matrix.getNumberOfRows(), matrix.getNumberOfColumns()));
187
188        Matrix3D<Q> M = Matrix3D.newInstance(unit);
189        M._data = Float64Matrix.valueOf(matrix);
190
191        return M;
192    }
193
194    /**
195     * Returns a {@link Matrix3D} instance containing the specified matrix of double
196     * values stated in the specified units. The input matrix must have dimensions of 3x3.
197     *
198     * @param matrix the matrix of double values stated in the specified unit (must have
199     *               dimension of 3x3).
200     * @param unit   the unit in which the elements are stated.
201     * @return the matrix having the specified values.
202     */
203    public static <Q extends Quantity> Matrix3D<Q> valueOf(double[][] matrix, Unit<Q> unit) {
204        if (matrix.length != 3 || matrix[0].length != 3)
205            throw new DimensionException(MessageFormat.format(RESOURCES.getString("3x3MatrixExpectedErr"),
206                    matrix.length, matrix[0].length));
207
208        Matrix3D<Q> M = Matrix3D.newInstance(unit);
209        M._data = Float64Matrix.valueOf(matrix);
210
211        return M;
212    }
213
214    /**
215     * Returns a 3D matrix equivalent to the specified matrix. All the values are
216     * converted to the same units as the 1st element (row=0,column=0).
217     *
218     * @param that the matrix to convert.
219     * @return <code>that</code> or a 3D matrix holding the same elements as the specified
220     *         matrix.
221     */
222    public static <Q extends Quantity> Matrix3D<Q> valueOf(Matrix<Parameter<Q>> that) {
223        if (that instanceof Matrix3D)
224            return (Matrix3D<Q>)that;
225
226        if (that.getNumberOfColumns() != 3 || that.getNumberOfRows() != 3)
227            throw new DimensionException(MessageFormat.format(RESOURCES.getString("3x3MatrixExpectedErr"),
228                    that.getNumberOfRows(), that.getNumberOfColumns()));
229
230        Vector3D<Q> row0 = Vector3D.valueOf(that.getRow(0));
231        Vector3D<Q> row1 = Vector3D.valueOf(that.getRow(1));
232        Vector3D<Q> row2 = Vector3D.valueOf(that.getRow(2));
233
234        return Matrix3D.valueOf(row0, row1, row2);
235    }
236
237    /**
238     * Returns the number of rows m for this matrix. This implementation always returns 3.
239     */
240    @Override
241    public int getNumberOfRows() {
242        return 3;
243    }
244
245    /**
246     * Returns the number of columns n for this matrix. This implementation always returns
247     * 3.
248     */
249    @Override
250    public int getNumberOfColumns() {
251        return 3;
252    }
253
254    /**
255     * Returns the unit in which the {@link #get values} in this vector are stated.
256     */
257    public Unit<Q> getUnit() {
258        return _unit;
259    }
260
261    /**
262     * Returns the equivalent to this matrix but stated in the specified unit.
263     *
264     * @param unit the unit of the matrix to be returned.
265     * @return a matrix equivalent to this vector but stated in the specified unit.
266     * @throws ConversionException if the current model does not allows for conversion to
267     * the specified unit.
268     */
269    public <R extends Quantity> Matrix3D<R> to(Unit<R> unit) {
270        if ((_unit == unit) || this._unit.equals(unit))
271            return (Matrix3D<R>)this;
272
273        UnitConverter cvtr = Parameter.converterOf(_unit, unit);
274        if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary.
275            Matrix3D<R> result = (Matrix3D<R>)Matrix3D.copyOf(this);
276            result._unit = unit;
277            return result;
278        }
279
280        double a = cvtr.convert(_data.get(0, 0).doubleValue());
281        double b = cvtr.convert(_data.get(0, 1).doubleValue());
282        double c = cvtr.convert(_data.get(0, 2).doubleValue());
283        double d = cvtr.convert(_data.get(1, 0).doubleValue());
284        double e = cvtr.convert(_data.get(1, 1).doubleValue());
285        double f = cvtr.convert(_data.get(1, 2).doubleValue());
286        double g = cvtr.convert(_data.get(2, 0).doubleValue());
287        double h = cvtr.convert(_data.get(2, 1).doubleValue());
288        double i = cvtr.convert(_data.get(2, 2).doubleValue());
289
290        Matrix3D<R> M = Matrix3D.valueOf(a, b, c, d, e, f, g, h, i, unit);
291        return M;
292    }
293
294    /**
295     * Casts this Matrix3D to a parameterized unit of specified nature or throw a
296     * <code>ClassCastException</code> if the specified quantity and this matrix unit
297     * dimension do not match.
298     *
299     * @param type the quantity class identifying the nature of the unit.
300     * @return this matrix parameterized with the specified type.
301     * @throws ClassCastException if the dimension of this parameter's unit is different
302     * from the specified quantity dimension.
303     * @throws UnsupportedOperationException if the specified quantity class does not have
304     * a public static field named "UNIT" holding the standard unit for the quantity.
305     */
306    public <T extends Quantity> Matrix3D<T> asType(Class<T> type) throws ClassCastException {
307        Unit<T> u = _unit.asType(type); //  If no exception is thrown, the cast is valid.
308        return (Matrix3D<T>)this;
309    }
310
311    /**
312     * Returns a single element from this matrix.
313     *
314     * @param i the row index (range [0..3[).
315     * @param j the column index (range [0..3[).
316     * @return the element read at [i,j].
317     */
318    @Override
319    public Parameter<Q> get(int i, int j) {
320        return Parameter.valueOf(_data.get(i, j).doubleValue(), _unit);
321    }
322
323    /**
324     * Returns a single element from this matrix as a <code>double</code>.
325     *
326     * @param i the row index (range [0..3[).
327     * @param j the column index (range [0..3[).
328     * @return the element read at [i,j].
329     */
330    public double getValue(int i, int j) {
331        return _data.get(i, j).doubleValue();
332    }
333
334    /**
335     * Returns the row identified by the specified index in this matrix.
336     *
337     * @param i the row index (range [0..3[).
338     */
339    @Override
340    public Vector3D<Q> getRow(int i) {
341        return Vector3D.valueOf(_data.getRow(i), _unit);
342    }
343
344    /**
345     * Returns the column identified by the specified index in this matrix.
346     *
347     * @param j the column index (range [0..3[).
348     */
349    @Override
350    public Vector3D<Q> getColumn(int j) {
351        return Vector3D.valueOf(_data.getColumn(j), _unit);
352    }
353
354    /**
355     * Returns the diagonal vector.
356     *
357     * @return the vector holding the diagonal elements.
358     */
359    @Override
360    public Vector3D<Q> getDiagonal() {
361        return Vector3D.valueOf(_data.getDiagonal(), _unit);
362    }
363
364    /**
365     * Returns the negation of this matrix.
366     *
367     * @return <code>-this</code>
368     */
369    @Override
370    public Matrix3D<Q> opposite() {
371        Matrix3D<Q> M = Matrix3D.newInstance(_unit);
372        M._data = _data.opposite();
373        return M;
374    }
375
376    /**
377     * Returns the sum of this matrix with the one specified.
378     *
379     * @param that the matrix to be added.
380     * @return <code>this + that</code>
381     * @throws DimensionException if the matrix dimensions are different.
382     */
383    @Override
384    public Matrix3D<Q> plus(Matrix<Parameter<Q>> that) {
385
386        //  Convert input matrix to a Float64Matrix (with unit conversion if necessary).
387        Float64Matrix thatData = toFloat64Matrix(that, this._unit);
388
389        Matrix3D<Q> M = Matrix3D.newInstance(_unit);
390        M._data = this._data.plus(thatData);
391
392        return M;
393    }
394
395    /**
396     * Returns the difference between this matrix and the one specified.
397     *
398     * @param that the matrix to be subtracted from this one.
399     * @return <code>this - that</code>
400     * @throws DimensionException if the matrix dimensions are different.
401     */
402    @Override
403    public Matrix3D<Q> minus(Matrix<Parameter<Q>> that) {
404
405        //  Convert input matrix to a Float64Matrix (with unit conversion if necessary).
406        Float64Matrix thatData = toFloat64Matrix(that, this._unit);
407
408        Matrix3D<Q> M = Matrix3D.newInstance(_unit);
409        M._data = this._data.minus(thatData);
410
411        return M;
412    }
413
414    /**
415     * Returns the product of this matrix by the specified factor.
416     *
417     * @param k the coefficient multiplier
418     * @return <code>this · k</code>
419     */
420    @Override
421    public Matrix3D times(Parameter k) {
422        Matrix3D<?> M = FACTORY.object();
423
424        //  Find the combined units.
425        M._unit = (Unit)Parameter.productOf(this.getUnit(), k.getUnit());
426        M._data = this._data.times(Float64.valueOf(k.getValue()));
427
428        return M;
429    }
430
431    /**
432     * Returns the product of this matrix by the specified factor.
433     *
434     * @param k the coefficient multiplier
435     * @return <code>this · k</code>
436     */
437    public Matrix3D<Q> times(double k) {
438        Matrix3D<Q> M = FACTORY.object();
439
440        M._unit = this._unit;
441        M._data = this._data.times(Float64.valueOf(k));
442
443        return M;
444    }
445
446    /**
447     * Returns the product of this matrix by the specified vector.
448     *
449     * @param v the vector.
450     * @return <code>this · v</code>
451     * @throws DimensionException - if v.getDimension() != this.getNumberOfColumns()
452     */
453    @Override
454    public Vector3D times(Vector v) {
455
456        //  Make sure the input vector is a Vector3D instance.
457        Vector3D T = Vector3D.valueOf(v);
458
459        //  Find the combined units.
460        Unit unit = Parameter.productOf(this.getUnit(), ((Parameter)v.get(0)).getUnit());
461
462        //  Multiply the vector and the matrix.
463        Float64Vector V = _data.times(T.toFloat64Vector());
464
465        return Vector3D.valueOf(V, unit);
466    }
467
468    /**
469     * Returns the product of this matrix with the one specified.
470     *
471     * @param that the matrix multiplier.
472     * @return <code>this · that</code>
473     * @throws DimensionException - if this.getNumberOfColumns() !=
474     * that.getNumberOfRows().
475     */
476    @Override
477    public Matrix3D times(Matrix that) {
478
479        //  Convert input matrix to a Float64Matrix.
480        Float64Matrix thatData = toFloat64Matrix(that, null);
481
482        //  Find the combined units.
483        Unit unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(0, 0)).getUnit());
484
485        Matrix3D M = Matrix3D.newInstance(unit);
486        M._data = this._data.times(thatData);
487
488        return M;
489    }
490
491    /**
492     * Returns the inverse of this matrix. If the matrix is singular, the
493     * {@link #determinant()} will be zero (or nearly zero).
494     *
495     * @return <code>1 / this</code>
496     */
497    @Override
498    public Matrix3D inverse() {
499        Unit unit = _unit.inverse();
500
501        Matrix3D M = Matrix3D.newInstance(unit);
502        M._data = _data.inverse();
503
504        return M;
505    }
506
507    /**
508     * Returns the determinant of this matrix.
509     *
510     * @return this matrix determinant.
511     */
512    @Override
513    public Parameter determinant() {
514        Unit unit = _unit.pow(3);
515
516        Parameter P = Parameter.valueOf(_data.determinant().doubleValue(), unit);
517
518        return P;
519    }
520
521    /**
522     * Returns the transpose of this matrix.
523     *
524     * @return <code>A'</code>
525     */
526    @Override
527    public Matrix3D<Q> transpose() {
528
529        Matrix3D<Q> M = Matrix3D.newInstance(_unit);
530        M._data = _data.transpose();
531
532        return M;
533    }
534
535    /**
536     * Returns the cofactor of an element in this matrix. It is the value obtained by
537     * evaluating the determinant formed by the elements not in that particular row or
538     * column.
539     *
540     * @param i the row index.
541     * @param j the column index.
542     * @return the cofactor of THIS[i,j].
543     */
544    @Override
545    public Parameter cofactor(int i, int j) {
546        Unit unit = _unit.pow(2);
547
548        Parameter P = Parameter.valueOf(_data.cofactor(i, j).doubleValue(), unit);
549
550        return P;
551    }
552
553    /**
554     * Returns the adjoint of this matrix. It is obtained by replacing each element in
555     * this matrix with its cofactor and applying a + or - sign according to (-1)**(i+j),
556     * and then finding the transpose of the resulting matrix.
557     *
558     * @return the adjoint of this matrix.
559     */
560    @Override
561    public Matrix3D adjoint() {
562        Unit unit = _unit.pow(2);
563
564        Matrix3D M = Matrix3D.newInstance(unit);
565        M._data = _data.adjoint();
566
567        return M;
568    }
569
570    /**
571     * Returns the linear algebraic matrix tensor product of this matrix and another
572     * (Kronecker product).
573     *
574     * @param that the second matrix
575     * @return <code>this times that</code>
576     * @see <a href="http://en.wikipedia.org/wiki/Kronecker_product">
577     * Wikipedia: Kronecker Product</a>
578     */
579    @Override
580    public Matrix tensor(Matrix that) {
581        StackContext.enter();
582        try {
583            Float64Matrix thatData;
584
585            //  Convert "that" into a matrix of Float64 values.
586            if (that instanceof Matrix3D) {
587                Matrix3D T = (Matrix3D)that;
588                thatData = T._data;
589
590            } else {
591                FastTable<Float64Vector> rows = FastTable.newInstance();
592                FastTable<Float64> rowi = FastTable.newInstance();
593                int numCols = that.getNumberOfColumns();
594                int numRows = that.getNumberOfRows();
595                for (int i = 0; i < numRows; ++i) {
596                    for (int j = 0; j < numCols; ++j) {
597                        Parameter p = (Parameter)that.get(i, j);
598                        rowi.add(Float64.valueOf(p.getValue()));
599                    }
600                    rows.add(Float64Vector.valueOf(rowi));
601                    rowi.clear();
602                }
603
604                thatData = Float64Matrix.valueOf(rows);
605            }
606
607            Unit unit = Parameter.productOf(this.getUnit(), ((Parameter)that.get(0, 0)).getUnit());
608
609            Float64Matrix matrix = _data.tensor(thatData);
610
611            FastTable rows = FastTable.newInstance();
612            FastTable<Parameter> rowi = FastTable.newInstance();
613            int numRows = matrix.getNumberOfRows();
614            int numCols = matrix.getNumberOfColumns();
615            for (int i = 0; i < numRows; ++i) {
616                for (int j = 0; j < numCols; ++j) {
617                    Parameter p = Parameter.valueOf(matrix.get(i, j).doubleValue(), unit);
618                    rowi.add(p);
619                }
620                rows.add(DenseVector.valueOf(rowi));
621                rowi.clear();
622            }
623
624            DenseMatrix M = DenseMatrix.valueOf(rows);
625
626            return StackContext.outerCopy(M);
627
628        } finally {
629            StackContext.exit();
630        }
631    }
632
633    /**
634     * Compares this Matrix3D for approximate equality to zero (all the values are within
635     * the numerical roundoff error of zero).
636     *
637     * @return <code>true</code> if this Matrix3D is approximately equal to zero;
638     *         <code>false</code> otherwise.
639     */
640    public boolean isApproxZero() {
641
642        for (int i = 0; i < 3; ++i) {
643            for (int j = 0; j < 3; ++j) {
644                double value = this.getValue(i, j);
645                if (MathLib.abs(value) > Parameter.EPS)
646                    return false;
647            }
648        }
649
650        return true;
651    }
652
653    /**
654     * Returns the vectorization of this matrix. The vectorization of a matrix is the
655     * column vector obtained by stacking the columns of the matrix on top of one another.
656     * The default implementation returns a <code>DenseVector<Parameter<?>></code>.
657     *
658     * @return the vectorization of this matrix.
659     */
660    @Override
661    public Vector<Parameter<Q>> vectorization() {
662        Float64Vector V = _data.vectorization();
663        FastTable<Parameter<Q>> list = FastTable.newInstance();
664
665        int num = V.getDimension();
666        for (int i = 0; i < num; ++i) {
667            double value = V.getValue(i);
668            Parameter<Q> p = Parameter.valueOf(value, _unit);
669            list.add(p);
670        }
671        return DenseVector.valueOf(list);
672    }
673
674    /**
675     * Returns a copy of this matrix allocated by the calling thread (possibly on the
676     * stack).
677     *
678     * @return an identical and independent copy of this matrix.
679     */
680    @Override
681    public Matrix3D<Q> copy() {
682        return Matrix3D.copyOf(this);
683    }
684
685    /**
686     * Returns a Float64Matrix containing the data in this matrix in the current units of
687     * this matrix.
688     */
689    public Float64Matrix toFloat64Matrix() {
690        return _data;
691    }
692
693    /**
694     * Convert a matrix of parameter objects to a Float64Matrix stated in the specified
695     * units.
696     */
697    private static <Q extends Quantity> Float64Matrix toFloat64Matrix(Matrix<Parameter<Q>> that, Unit<Q> unit) {
698
699        //  Make sure input is a Matrix3D instance.
700        Matrix3D<Q> T = Matrix3D.valueOf(that);
701
702        //  Convert that vector's units to the specified units if necessary.
703        Float64Matrix thatData = T._data;
704        if ((unit != null) && (unit != T._unit) && !unit.equals(T._unit)) {
705            UnitConverter cvtr = Parameter.converterOf(T._unit, unit);
706            if (cvtr != UnitConverter.IDENTITY) {
707                double a = cvtr.convert(thatData.get(0, 0).doubleValue());
708                double b = cvtr.convert(thatData.get(0, 1).doubleValue());
709                double c = cvtr.convert(thatData.get(0, 2).doubleValue());
710                double d = cvtr.convert(thatData.get(1, 0).doubleValue());
711                double e = cvtr.convert(thatData.get(1, 1).doubleValue());
712                double f = cvtr.convert(thatData.get(1, 2).doubleValue());
713                double g = cvtr.convert(thatData.get(2, 0).doubleValue());
714                double h = cvtr.convert(thatData.get(2, 1).doubleValue());
715                double i = cvtr.convert(thatData.get(2, 2).doubleValue());
716                Float64Vector row0 = Float64Vector.valueOf(a, b, c);
717                Float64Vector row1 = Float64Vector.valueOf(d, e, f);
718                Float64Vector row2 = Float64Vector.valueOf(g, h, i);
719                thatData = Float64Matrix.valueOf(row0, row1, row2);
720            }
721        }
722
723        return thatData;
724    }
725
726    /**
727     * During serialization, this will write out the Float64Matrix as a simple series of
728     * <code>double</code> values. This method is ONLY called by the Java Serialization
729     * mechanism and should not otherwise be used.
730     */
731    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
732
733        // Call the default write object method.
734        out.defaultWriteObject();
735
736        //  Write out the three coordinate values.
737        for (int i = 0; i < 3; ++i)
738            for (int j = 0; j < 3; ++j)
739                out.writeDouble(_data.get(i, j).doubleValue());
740
741    }
742
743    /**
744     * During de-serialization, this will handle the reconstruction of the Float64Matrix.
745     * This method is ONLY called by the Java Serialization mechanism and should not
746     * otherwise be used.
747     */
748    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
749
750        // Call the default read object method.
751        in.defaultReadObject();
752
753        //  Read in the three coordinate values.
754        FastTable<Float64Vector> rows = FastTable.newInstance();
755        for (int i = 0; i < 3; ++i) {
756            double v1 = in.readDouble();
757            double v2 = in.readDouble();
758            double v3 = in.readDouble();
759            Float64Vector row = Float64Vector.valueOf(v1, v2, v3);
760            rows.add(row);
761        }
762
763        _data = Float64Matrix.valueOf(rows);
764        FastTable.recycle(rows);
765
766    }
767
768    ///////////////////////
769    // Factory creation. //
770    ///////////////////////
771    private Matrix3D() { }
772
773    private static final ObjectFactory<Matrix3D> FACTORY = new ObjectFactory<Matrix3D>() {
774        @Override
775        protected Matrix3D create() {
776            return new Matrix3D();
777        }
778    };
779
780    private static <Q extends Quantity> Matrix3D<Q> newInstance(Unit<Q> unit) {
781        Matrix3D<Q> measure = FACTORY.object();
782        measure._unit = unit;
783        return measure;
784    }
785
786    private static <Q extends Quantity> Matrix3D<Q> copyOf(Matrix3D<Q> original) {
787        Matrix3D<Q> measure = Matrix3D.newInstance(original._unit);
788        measure._data = original._data.copy();
789        return measure;
790    }
791
792    /**
793     * Tests the methods in this class.
794     */
795    public static void main(String args[]) {
796        System.out.println("Testing Matrix3D:");
797
798        Matrix3D<Length> m1 = Matrix3D.valueOf(1, 2, 3, 4, 5, 6, 7, 8, 9, SI.METER);
799        System.out.println("m1 = \n" + m1);
800        System.out.println("  converted to feet  = \n" + m1.to(NonSI.FOOT));
801        System.out.println("  m1.getRow(1) = " + m1.getRow(1));
802        System.out.println("  m1.getColumn(2) = " + m1.getColumn(2));
803        System.out.println("  m1.getDiagonal() = " + m1.getDiagonal());
804        System.out.println("  m1.opposite() = \n" + m1.opposite());
805        System.out.println("  m1.transpose() = \n" + m1.transpose());
806        System.out.println("  m1.vectorization() = \n" + m1.vectorization());
807        System.out.println("  m1.determinant() = " + m1.determinant() + "; matrix is singular and can not be inverted");
808
809        Parameter<Length> p1 = Parameter.valueOf(2, SI.METER);
810        System.out.println("\np1 = " + p1);
811        System.out.println("  m1.times(p1) = \n" + m1.times(p1));
812
813        Matrix3D<Length> m2 = Matrix3D.valueOf(1, 0, 1, 0, 1, 0, -1, 0, 1, SI.METER);
814        System.out.println("\nm2 = \n" + m2);
815        System.out.println("  m2.determinant() = " + m2.determinant());
816        System.out.println("  m2.inverse() = \n" + m2.inverse());
817        System.out.println("  m2.cofactor(0,2) = " + m2.cofactor(0, 2));
818        System.out.println("  m2.adjoint() = \n" + m2.adjoint());
819        System.out.println("  m1.times(m2) = \n" + m1.times(m2));
820        System.out.println("  m1.tensor(m2) = \n" + m1.tensor(m2));
821
822        Vector3D<Length> v1 = Vector3D.valueOf(1, 2, 3, SI.METER);
823        System.out.println("\nv1 = " + v1);
824        System.out.println("  m1.times(v1) = " + m1.times(v1));
825
826        //  Write out XML data.
827        try {
828            System.out.println();
829
830            // Creates some useful aliases for class names.
831            javolution.xml.XMLBinding binding = new javolution.xml.XMLBinding();
832            binding.setAlias(jahuwaldt.js.param.Parameter.class, "Parameter");
833
834            javolution.xml.XMLObjectWriter writer = javolution.xml.XMLObjectWriter.newInstance(System.out);
835            writer.setIndentation("    ");
836            writer.setBinding(binding);
837            writer.write(m2, "Matrix3D", Matrix3D.class);
838            writer.flush();
839
840            System.out.println();
841        } catch (Exception e) {
842            e.printStackTrace();
843        }
844    }
845
846}