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