1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.math.linear.matrix;
26 
27 import std.math;
28 import std.traits;
29 import std.algorithm;
30 import std.range;
31 import std.array;
32 import std.exception;
33 
34 import des.util.testsuite;
35 
36 import des.math.util;
37 import des.math.linear.vector;
38 import des.math.linear.quaterni;
39 import des.math.basic.traits;
40 
41 ///
42 template isMatrix(E)
43 {
44     enum isMatrix = is( typeof( impl(E.init) ) );
45     void impl(size_t H,size_t W,T)( Matrix!(H,W,T) ){}
46 }
47 
48 ///
49 template isStaticMatrix(E)
50 {
51     static if( !isMatrix!E )
52         enum isStaticMatrix = false;
53     else enum isStaticMatrix = E.isStatic;
54 }
55 
56 ///
57 template isDynamicMatrix(E)
58 {
59     static if( !isMatrix!E )
60         enum isDynamicMatrix = false;
61     else enum isDynamicMatrix = E.isDynamic;
62 }
63 
64 unittest
65 {
66     static assert( !isStaticMatrix!float );
67     static assert( !isDynamicMatrix!float );
68 }
69 
70 private @property
71 {
72     import std.string;
73 
74     string identityMatrixDataString(size_t S)()
75     { return diagMatrixDataString!(S,S)(1); }
76 
77     string zerosMatrixDataString(size_t H, size_t W)()
78     { return diagMatrixDataString!(H,W)(0); }
79 
80     string diagMatrixDataString(size_t H, size_t W)( size_t val )
81     {
82         string[] ret;
83         foreach( i; 0 .. H )
84         {
85             string[] buf;
86             foreach( j; 0 .. W )
87                 buf ~= format( "%d", i == j ? val : 0 );
88             ret ~= "[" ~ buf.join(",") ~ "]";
89         }
90         return "[" ~ ret.join(",") ~ "]";
91     }
92 
93     unittest
94     {
95         assert( identityMatrixDataString!3 == "[[1,0,0],[0,1,0],[0,0,1]]" );
96         assert( zerosMatrixDataString!(3,3) == "[[0,0,0],[0,0,0],[0,0,0]]" );
97     }
98 
99     string castArrayString(string type, size_t H, size_t W)()
100     {
101         return format( "cast(%s[%d][%d])", type, W, H );
102     }
103 
104 }
105 
106 /++
107  +/
108 struct Matrix(size_t H, size_t W, E)
109 {
110     ///
111     alias selftype = Matrix!(H,W,E);
112 
113     ///
114     alias datatype = E;
115 
116     /// `H == 0 || W == 0`
117     enum bool isDynamic = H == 0 || W == 0;
118     /// `H != 0 && W != 0`
119     enum bool isStatic = H != 0 && W != 0;
120 
121     /// `H == 0`
122     enum bool isDynamicHeight = H == 0;
123     /// `H != 0`
124     enum bool isStaticHeight = H != 0;
125 
126     /// `W == 0`
127     enum bool isDynamicWidth = W == 0;
128     /// `W != 0`
129     enum bool isStaticWidth = W != 0;
130 
131     /// `isStaticHeight && isDynamicWidth`
132     enum bool isStaticHeightOnly = isStaticHeight && isDynamicWidth;
133     /// `isStaticWidth && isDynamicHeight`
134     enum bool isStaticWidthOnly = isStaticWidth && isDynamicHeight;
135 
136     /// `isDynamicHeight && isDynamicWidth`
137     enum bool isDynamicAll = isDynamicHeight && isDynamicWidth;
138     /// `isStaticWidthOnly || isStaticHeightOnly`
139     enum bool isDynamicOne = isStaticWidthOnly || isStaticHeightOnly;
140 
141     static if( isStatic )
142     {
143         static if( isNumeric!E )
144         {
145             static if( H == W )
146                 /// static data ( if isNumeric!E fills valid numbers: if squred then identity matrix else zeros )
147                 E[W][H] data = mixin( castArrayString!("E",H,H) ~ identityMatrixDataString!H );
148             else 
149                 E[W][H] data = mixin( castArrayString!("E",H,W) ~ zerosMatrixDataString!(H,W) );
150         }
151         else E[W][H] data;
152     }
153     else static if( isStaticWidthOnly )
154         /// static width only
155         E[W][] data;
156     else static if( isStaticHeightOnly )
157         /// static height only
158         E[][H] data;
159     else
160         /// full dynamic
161         E[][] data;
162 
163     ///
164     alias data this;
165 
166 pure:
167 
168     private static bool allowSomeOp(size_t A, size_t B )
169     { return A==B || A==0 || B==0; }
170 
171     static if( isStatic || isDynamicOne )
172     {
173         /++ fill data and set dynamic size for `isDynamicOne` matrix
174 
175             only:
176                 if( isStatic || isDynamicOne )
177          +/
178         this(X...)( in X vals )
179             if( is(typeof(flatData!E(vals))) )
180         {
181             static if( isStatic )
182             {
183                 static if( hasNoDynamic!X )
184                 {
185                     static if( X.length > 1 )
186                     {
187                         static assert( getElemCount!X == W*H, "wrong args count" );
188                         static assert( isConvertable!(E,X), "wrong args type" );
189                         mixin( matrixStaticFill!("E","data","vals",W,E,X) );
190                     }
191                     else static if( X.length == 1 && isStaticMatrix!(X[0]) )
192                     {
193                         static assert( X[0].width == W && X[0].height == H );
194                         foreach( y; 0 .. H )
195                             foreach( x; 0 .. W )
196                                 data[y][x] = vals[0][y][x];
197                     }
198                     else enum __DF=true;
199                 }
200                 else enum __DF=true;
201 
202                 static if( is(typeof(__DF)) )
203                     _fillData( flatData!E(vals) );
204             }
205             else static if( isStaticWidthOnly )
206             {
207                 auto buf = flatData!E(vals);
208                 enforce( !(buf.length%W), "wrong args length" );
209                 resize( buf.length/W, W );
210                 _fillData( buf );
211             }
212             else static if( isStaticHeightOnly )
213             {
214                 auto buf = flatData!E(vals);
215                 enforce( !(buf.length%H), "wrong args length" );
216                 resize( H, buf.length/H );
217                 _fillData( buf );
218             }
219         }
220     }
221     else
222     {
223         /++ set size and fill data
224 
225             only:
226                 if( isDynamicAll )
227          +/
228         this(X...)( size_t iH, size_t iW, in X vals )
229         {
230             auto buf = flatData!E(vals);
231             enforce( buf.length == iH * iW || buf.length == 1, "wrong args length" );
232             resize( iH, iW );
233             _fillData( buf );
234         }
235 
236         /++ set size
237 
238             only:
239                 if( isDynamicAll )
240          +/
241         this( size_t iH, size_t iW ) { resize( iH, iW ); }
242     }
243 
244     ///
245     this(size_t oH, size_t oW, X)( in Matrix!(oH,oW,X) mtr )
246         if( is( typeof( E(X.init) ) ) )
247     {
248         static if( isDynamic )
249             resize( mtr.height, mtr.width );
250         foreach( i; 0 .. mtr.height )
251             foreach( j; 0 .. mtr.width )
252                 data[i][j] = E(mtr[i][j]);
253     }
254 
255     static if( isDynamic )
256     {
257         /++
258             only:
259                 if( isDynamic )
260          +/
261         this(this)
262         {
263             data = data.dup;
264             foreach( ref row; data )
265                 row = row.dup;
266         }
267 
268         /++
269             only:
270                 if( isDynamic )
271          +/
272         ref typeof(this) opAssign(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) b )
273             if( allowSomeOp(H,bH) && allowSomeOp(W,bW) && is(typeof(E(X.init))) )
274         {
275             static if( isStaticHeight && b.isDynamicHeight ) enforce( height == b.height, "wrong height" );
276             static if( isStaticWidth && b.isDynamicWidth ) enforce( width == b.width, "wrong width" );
277             static if( isDynamic ) resize(b.height,b.width);
278             _fillData(flatData!E(b.data));
279             return this;
280         }
281     }
282 
283     private void _fillData( in E[] vals... )
284     {
285         enforce( vals.length > 0, "no vals to fill" );
286         if( vals.length > 1 )
287         {
288             enforce( vals.length == height * width, "wrong vals length" );
289             size_t k = 0;
290             foreach( ref row; data )
291                 foreach( ref v; row )
292                     v = vals[k++];
293         }
294         else foreach( ref row; data ) row[] = vals[0];
295     }
296 
297     /// fill data
298     ref typeof(this) fill( in E[] vals... )
299     {
300         _fillData( vals );
301         return this;
302     }
303 
304     static if( (W==H && isStatic) || isDynamicOne )
305     {
306         /++ get diagonal matrix, diagonal elements fills cyclically
307 
308             only:
309                 if( (W==H && isStatic) || isDynamicOne )
310          +/
311         static auto diag(X...)( in X vals )
312         if( X.length > 0 && is(typeof(E(0))) && is(typeof(flatData!E(vals))) )
313         {
314             selftype ret;
315             static if( isStaticHeight ) auto L = H;
316             else auto L = W;
317 
318             static if( ret.isDynamic )
319                 ret.resize(L,L);
320 
321             ret._fillData( E(0) );
322             ret.fillDiag( flatData!E(vals) );
323 
324             return ret;
325         }
326     }
327 
328     ///
329     ref typeof(this) fillDiag( in E[] vals... )
330     {
331         enforce( vals.length > 0, "no vals to fill" );
332         enforce( height == width, "not squared" );
333         size_t k = 0;
334         foreach( i; 0 .. height )
335             data[i][i] = vals[k++%$];
336         return this;
337     }
338 
339     static if( isStaticHeight )
340         /++
341             only:
342                 if( isStaticHeight )
343          +/
344         enum height = H;
345     else
346     {
347         /// if isStaticHeight it's enum (not available to set)
348         @property size_t height() const { return data.length; }
349         /// if isStaticHeight it's enum (not available to set)
350         @property size_t height( size_t nh )
351         {
352             resize( nh, width );
353             return nh;
354         }
355     }
356 
357     static if( isStaticWidth )
358         /++
359             only:
360                 if( isStaticWidth )
361          +/
362         enum width = W;
363     else
364     {
365         private size_t _width = W;
366         /// if isStaticWidth it's enum (not available to set)
367         @property size_t width() const { return _width; }
368         /// if isStaticWidth it's enum (not available to set)
369         @property size_t width( size_t nw )
370         {
371             resize( height, nw );
372             return nw;
373         }
374     }
375 
376     static if( isDynamic )
377     {
378         /++ resize dynamic matrix, new height/width must equals static height/width
379 
380             only:
381                 if( isDynamic )
382          +/
383         ref typeof(this) resize( size_t nh, size_t nw )
384         {
385             static if( isStaticHeight ) enforce( nh == H, "height is static" );
386             static if( isStaticWidth ) enforce( nw == W, "width is static" );
387 
388             static if( isDynamicHeight ) data.length = nh;
389             static if( isDynamicWidth )
390             {
391                 _width = nw;
392                 foreach( i; 0 .. height )
393                     data[i].length = nw;
394             }
395             return this;
396         }
397     }
398 
399     ///
400     auto expandHeight(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const
401         if( (bW==W||W==0||bW==0) && is(typeof(E(X.init))) )
402     {
403         static if( isDynamicWidth || mtr.isDynamicWidth )
404             enforce( mtr.width == width, "wrong width" );
405 
406         auto ret = Matrix!(0,W,E)(this);
407         auto last_height = height;
408         ret.resize( ret.height + mtr.height, width );
409         foreach( i; 0 .. mtr.height )
410             foreach( j; 0 .. width )
411                 ret.data[last_height+i][j] = E(mtr.data[i][j]);
412         return ret;
413     }
414 
415     ///
416     auto expandHeight(X...)( in X vals ) const
417         if( is(typeof(Matrix!(1,W,E)(vals))) )
418     { return expandHeight( Matrix!(1,W,E)(vals) ); }
419 
420     ///
421     auto expandWidth(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const
422         if( (bH==H||H==0||bH==0) && is(typeof(E(X.init))) )
423     {
424         static if( isDynamicHeight || mtr.isDynamicHeight )
425             enforce( mtr.height == height, "wrong height" );
426         auto ret = Matrix!(H,0,E)(this);
427         auto last_width = width;
428         ret.resize( height, ret.width + mtr.width );
429         foreach( i; 0 .. height )
430             foreach( j; 0 .. mtr.width )
431                 ret.data[i][last_width+j] = E(mtr.data[i][j]);
432         return ret;
433     }
434 
435     ///
436     auto expandWidth(X...)( in X vals ) const
437         if( is(typeof(Matrix!(H,1,E)(vals))) )
438     { return expandWidth( Matrix!(H,1,E)(vals) ); }
439 
440     ///
441     auto asArray() const @property
442     {
443         auto ret = new E[](width*height);
444         foreach( i; 0 .. height )
445             foreach( j; 0 .. width )
446                 ret[i*width+j] = data[i][j];
447         return ret;
448     }
449 
450     ///
451     auto sliceHeight( size_t start, size_t count=0 ) const
452     {
453         enforce( start < height );
454         count = count ? count : height - start;
455         auto ret = Matrix!(0,W,E)(this);
456         ret.resize( count, width );
457         foreach( i; 0 .. count )
458             ret.data[i][] = data[start+i][];
459         return ret;
460     }
461 
462     ///
463     auto sliceWidth( size_t start, size_t count=0 ) const
464     {
465         enforce( start < width );
466         count = count ? count : width - start;
467         auto ret = Matrix!(H,0,E)(this);
468         ret.resize( height, count );
469         foreach( i; 0 .. height )
470             foreach( j; 0 .. count )
471             ret.data[i][j] = data[i][start+j];
472         return ret;
473     }
474 
475     ///
476     auto opUnary(string op)() const
477         if( op == "-" && is( typeof( E.init * (-1) ) ) )
478     {
479         auto ret = selftype(this);
480         foreach( ref row; ret.data )
481             foreach( ref v; row )
482                 v = v * -1;
483         return ret;
484     }
485 
486     private void checkCompatible(size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const
487     {
488         static if( isDynamicHeight || mtr.isDynamicHeight )
489             enforce( height == mtr.height, "wrong height" );
490         static if( isDynamicWidth || mtr.isDynamicWidth )
491             enforce( width == mtr.width, "wrong width" );
492     }
493 
494     ///
495     auto opBinary(string op, size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const
496         if( (op=="+"||op=="-") && allowSomeOp(H,bH) && allowSomeOp(W,bW) )
497     {
498         checkCompatible( mtr );
499 
500         auto ret = selftype(this);
501 
502         foreach( i; 0 .. height )
503             foreach( j; 0 .. width )
504                 mixin( `ret[i][j] = E(data[i][j] ` ~ op ~ ` mtr[i][j]);` );
505         return ret;
506     }
507 
508     ///
509     auto opBinary(string op,X)( in X b ) const
510         if( (op!="+" && op!="-") && isValidOp!(op,E,X) )
511     {
512         auto ret = selftype(this);
513         foreach( i; 0 .. height )
514             foreach( j; 0 .. width )
515                 mixin( `ret[i][j] = E(data[i][j] ` ~ op ~ ` b);` );
516         return ret;
517     }
518 
519     ///
520     auto opBinary(string op,size_t bH, size_t bW, X)( in Matrix!(bH,bW,X) mtr ) const
521         if( op=="*" && allowSomeOp(W,bH) && isValidOp!("*",E,X) )
522     {
523         static if( isDynamic || mtr.isDynamic )
524             enforce( width == mtr.height, "incompatible sizes for mul" );
525         Matrix!(H,bW,E) ret;
526         static if( ret.isDynamic ) ret.resize(height,mtr.width);
527 
528         foreach( i; 0 .. height )
529             foreach( j; 0 .. mtr.width )
530             {
531                 ret[i][j] = E( data[i][0] * mtr.data[0][j] );
532                 foreach( k; 1 .. width )
533                     ret[i][j] = ret[i][j] + E( data[i][k] * mtr.data[k][j] );
534             }
535 
536         return ret;
537     }
538 
539     ///
540     ref typeof(this) opOpAssign(string op, E)( in E b )
541         if( mixin( `is( typeof( selftype.init ` ~ op ~ ` E.init ) == selftype )` ) )
542     { mixin( `return this = this ` ~ op ~ ` b;` ); }
543 
544     ///
545     bool opCast(E)() const if( is( E == bool ) )
546     { 
547         foreach( v; asArray ) if( !isFinite(v) ) return false;
548         return true;
549     }
550 
551     /// transponate
552     auto T() const @property
553     {
554         Matrix!(W,H,E) ret;
555         static if( isDynamic ) ret.resize(width,height);
556         foreach( i; 0 .. height)
557             foreach( j; 0 .. width )
558                 ret[j][i] = data[i][j];
559         return ret;
560     }
561 
562     ///
563     ref typeof(this) setCol(X...)( size_t no, in X vals )
564         if( is(typeof(flatData!E(vals))) )
565     {
566         enforce( no < width, "bad col index" );
567         auto buf = flatData!E(vals);
568         enforce( buf.length == height, "bad data length" );
569         foreach( i; 0 .. height )
570             data[i][no] = buf[i];
571         return this;
572     }
573 
574     ///
575     ref typeof(this) setRow(X...)( size_t no, in X vals )
576         if( is(typeof(flatData!E(vals))) )
577     {
578         enforce( no < height, "bad row index" );
579         auto buf = flatData!E(vals);
580         enforce( buf.length == width, "bad data length" );
581         data[no][] = buf[];
582         return this;
583     }
584 
585     ///
586     ref typeof(this) setRect(size_t bH, size_t bW, X)( size_t pos_row, size_t pos_col, in Matrix!(bH,bW,X) mtr )
587         if( is(typeof(E(X.init))) )
588     {
589         enforce( pos_row < height, "bad row index" );
590         enforce( pos_col < width, "bad col index" );
591         enforce( pos_row + mtr.height <= height, "bad height size" );
592         enforce( pos_col + mtr.width <= width, "bad width size" );
593 
594         foreach( i; 0 .. mtr.height )
595             foreach( j; 0 .. mtr.width )
596                 data[i+pos_row][j+pos_col] = E(mtr[i][j]);
597 
598         return this;
599     }
600 
601     ///
602     auto col( size_t no ) const
603     {
604         enforce( no < width, "bad col index" );
605         Matrix!(H,1,E) ret;
606         static if( ret.isDynamic )
607             ret.resize(height,1);
608         foreach( i; 0 .. height )
609             ret[i][0] = data[i][no];
610         return ret;
611     }
612 
613     ///
614     auto row( size_t no ) const
615     {
616         enforce( no < height, "bad row index" );
617         Matrix!(1,W,E) ret;
618         static if( ret.isDynamic )
619             ret.resize(1,width);
620         ret[0][] = data[no][];
621         return ret;
622     }
623 
624     ///
625     auto opBinary(string op,size_t K,X,alias string AS)( in Vector!(K,X,AS) v ) const
626         if( op=="*" && allowSomeOp(W,K) && isValidOp!("*",E,X) && isValidOp!("+",E,E) )
627     {
628         static if( isDynamic || v.isDynamic )
629             enforce( width == v.length, "wrong vector length" );
630 
631         static if( isStatic && W == H )
632             Vector!(H,E,AS) ret;
633         else
634             Vector!(H,E) ret;
635 
636         static if( ret.isDynamic )
637             ret.length = height;
638 
639         foreach( i; 0 .. height )
640         {
641             ret[i] = data[i][0] * v[0];
642 
643             foreach( j; 1 .. width )
644                 ret[i] = ret[i] + E(data[i][j] * v[j]);
645         }
646 
647         return ret;
648     }
649 
650     ///
651     auto opBinaryRight(string op,size_t K,X,alias string AS)( in Vector!(K,X,AS) v ) const
652         if( op=="*" && isVector!(typeof(selftype.init.T * typeof(v).init)) )
653     { return this.T * v; }
654 
655     static private size_t[] getIndexesWithout(size_t max, in size_t[] arr)
656     {
657         size_t[] ret;
658         foreach( i; 0 .. max ) if( !canFind(arr,i) ) ret ~= i;
659         return ret;
660     }
661 
662     ///
663     auto subWithout( size_t[] without_rows=[], size_t[] without_cols=[] ) const
664     {
665         auto with_rows = getIndexesWithout(height,without_rows);
666         auto with_cols = getIndexesWithout(width,without_cols);
667 
668         return sub( with_rows,with_cols );
669     }
670 
671     /// get sub matrix
672     auto sub( size_t[] with_rows, size_t[] with_cols ) const
673     {
674         auto wrows = array( uniq(with_rows) );
675         auto wcols = array( uniq(with_cols) );
676 
677         enforce( all!(a=>a<height)( wrows ), "bad row index" );
678         enforce( all!(a=>a<width)( wcols ), "bad col index" );
679 
680         Matrix!(0,0,E) ret;
681         ret.resize( wrows.length, wcols.length );
682 
683         foreach( i, orig_i; wrows )
684             foreach( j, orig_j; wcols )
685                 ret[i][j] = data[orig_i][orig_j];
686 
687         return ret;
688     }
689 
690     static if( isFloatingPoint!E && ( isDynamicAll ||
691                 ( isStaticHeightOnly && H > 1 ) ||
692                 ( isStaticWidthOnly && W > 1 ) ||
693                 ( isStatic && W == H ) ) )
694     {
695         ///
696         E cofactor( size_t i, size_t j ) const
697         { return subWithout([i],[j]).det * coef(i,j); }
698 
699         private static nothrow @trusted auto coef( size_t i, size_t j )
700         { return ((i+j)%2?-1:1); }
701         
702         ///
703         auto det() const @property 
704         {
705             static if( isDynamic )
706                 enforce( width == height, "not square matrix" );
707 
708             static if( isDynamic )
709             {
710                 if( width == 1 )
711                     return data[0][0];
712                 else if( width == 2 )
713                     return data[0][0] * data[1][1] -
714                            data[0][1] * data[1][0];
715                 else return classicDet;
716             }
717             else
718             {
719                 static if( W == 1 )
720                     return data[0][0];
721                 else static if( W == 2 )
722                     return data[0][0] * data[1][1] -
723                            data[0][1] * data[1][0];
724                 else return classicDet;
725             }
726         }
727 
728         private @property classicDet() const
729         {
730             auto i = 0UL; // TODO: find max zeros line
731             auto ret = data[i][0] * cofactor(i,0);
732 
733             foreach( j; 1 .. width )
734                 ret = ret + data[i][j] * cofactor(i,j);
735 
736             return ret;
737         }
738 
739         ///
740         auto inv() const @property
741         {
742             static if( isDynamic )
743                 enforce( width == height, "not square matrix" );
744             else static assert( W==H, "not square matrix" );
745 
746             selftype buf;
747 
748             static if( isDynamic )
749                 buf.resize(height,width);
750 
751             foreach( i; 0 .. height )
752                 foreach( j; 0 .. width )
753                     buf[i][j] = cofactor(i,j);
754 
755             auto i = 0UL; // TODO: find max zeros line
756             auto d = data[i][0] * buf[i][0];
757 
758             foreach( j; 1 .. width )
759                 d = d + data[i][j] * buf[i][j];
760 
761             return buf.T / d;
762         }
763 
764         static if( (isStaticHeightOnly && H==4) ||
765                    (isStaticWidthOnly && W==4) ||
766                    (isStatic && H==W && H==4) ||
767                    isDynamicAll )
768         {
769             /++ only for transform matrix +/
770             @property auto speedTransformInv() const
771             {
772                 static if( isDynamic )
773                 {
774                     enforce( width == height, "not square matrix" );
775                     enforce( width == 4, "matrix must be 4x4" );
776                 }
777 
778                 selftype ret;
779                 static if( isDynamic )
780                     ret.resize( height, width );
781 
782                 foreach( i; 0 .. 3 )
783                     foreach( j; 0 .. 3 )
784                         ret[i][j] = this[j][i];
785 
786                 auto a22k = 1.0 / this[3][3];
787 
788                 ret[0][3] = -( ret[0][0] * this[0][3] + ret[0][1] * this[1][3] + ret[0][2] * this[2][3] ) * a22k;
789                 ret[1][3] = -( ret[1][0] * this[0][3] + ret[1][1] * this[1][3] + ret[1][2] * this[2][3] ) * a22k;
790                 ret[2][3] = -( ret[2][0] * this[0][3] + ret[2][1] * this[1][3] + ret[2][2] * this[2][3] ) * a22k;
791 
792                 ret[3][0] = -( this[3][0] * ret[0][0] + this[3][1] * ret[1][0] + this[3][2] * ret[2][0] ) * a22k;
793                 ret[3][1] = -( this[3][0] * ret[0][1] + this[3][1] * ret[1][1] + this[3][2] * ret[2][1] ) * a22k;
794                 ret[3][2] = -( this[3][0] * ret[0][2] + this[3][1] * ret[1][2] + this[3][2] * ret[2][2] ) * a22k;
795                 
796                 ret[3][3] = a22k * ( 1.0 - ( this[3][0] * ret[0][3] + this[3][1] * ret[1][3] + this[3][2] * ret[2][3] ) );
797 
798                 return ret;
799             }
800         }
801 
802         ///
803         auto rowReduceInv() const @property
804         {
805             static if( isDynamic )
806                 enforce( width == height, "not square matrix" );
807 
808             auto ln = height;
809 
810             auto orig = selftype(this);
811             selftype invt;
812             static if( isDynamic )
813             {
814                 invt.resize(ln,ln);
815                 foreach( i; 0 .. ln )
816                     foreach( j; 0 .. ln )
817                         invt[i][j] = E(i==j);
818             }
819 
820             foreach( r; 0 .. ln-1 )
821             {
822                 auto k = E(1) / orig[r][r];
823                 foreach( c; 0 .. ln )
824                 {
825                     orig[r][c] *= k;
826                     invt[r][c] *= k;
827                 }
828                 foreach( rr; r+1 .. ln )
829                 {
830                     auto v = orig[rr][r];
831                     foreach( c; 0 .. ln )
832                     {
833                         orig[rr][c] -= orig[r][c] * v;
834                         invt[rr][c] -= invt[r][c] * v;
835                     }
836                 }
837             }
838 
839             foreach_reverse( r; 1 .. ln )
840             {
841                 auto k = E(1) / orig[r][r];
842                 foreach( c; 0 .. ln )
843                 {
844                     orig[r][c] *= k;
845                     invt[r][c] *= k;
846                 }
847                 foreach_reverse( rr; 0 .. r )
848                 {
849                     auto v = orig[rr][r];
850                     foreach( c; 0 .. ln )
851                     {
852                         orig[rr][c] -= orig[r][c] * v;
853                         invt[rr][c] -= invt[r][c] * v;
854                     }
855                 }
856             }
857 
858             return invt;
859         }
860     }
861 }
862 
863 alias Matrix2(T)   = Matrix!(2,2,T); ///
864 alias Matrix2x3(T) = Matrix!(2,3,T); ///
865 alias Matrix2x4(T) = Matrix!(2,4,T); ///
866 alias Matrix2xD(T) = Matrix!(2,0,T); ///
867 alias Matrix3x2(T) = Matrix!(3,2,T); ///
868 alias Matrix3(T)   = Matrix!(3,3,T); ///
869 alias Matrix3x4(T) = Matrix!(3,4,T); ///
870 alias Matrix3xD(T) = Matrix!(3,0,T); ///
871 alias Matrix4x2(T) = Matrix!(4,2,T); ///
872 alias Matrix4x3(T) = Matrix!(4,3,T); ///
873 alias Matrix4(T)   = Matrix!(4,4,T); ///
874 alias Matrix4xD(T) = Matrix!(4,0,T); ///
875 alias MatrixDx2(T) = Matrix!(0,2,T); ///
876 alias MatrixDx3(T) = Matrix!(0,3,T); ///
877 alias MatrixDx4(T) = Matrix!(0,4,T); ///
878 alias MatrixDxD(T) = Matrix!(0,0,T); ///
879 
880 alias Matrix!(2,2,float) mat2;   ///
881 alias Matrix!(2,3,float) mat2x3; ///
882 alias Matrix!(2,4,float) mat2x4; ///
883 alias Matrix!(2,0,float) mat2xD; ///
884 alias Matrix!(3,2,float) mat3x2; ///
885 alias Matrix!(3,3,float) mat3;   ///
886 alias Matrix!(3,4,float) mat3x4; ///
887 alias Matrix!(3,0,float) mat3xD; ///
888 alias Matrix!(4,2,float) mat4x2; ///
889 alias Matrix!(4,3,float) mat4x3; ///
890 alias Matrix!(4,4,float) mat4;   ///
891 alias Matrix!(4,0,float) mat4xD; ///
892 alias Matrix!(0,2,float) matDx2; ///
893 alias Matrix!(0,3,float) matDx3; ///
894 alias Matrix!(0,4,float) matDx4; ///
895 alias Matrix!(0,0,float) matD;   ///
896 
897 alias Matrix!(2,2,double) dmat2;   ///
898 alias Matrix!(2,3,double) dmat2x3; ///
899 alias Matrix!(2,4,double) dmat2x4; ///
900 alias Matrix!(2,0,double) dmat2xD; ///
901 alias Matrix!(3,2,double) dmat3x2; ///
902 alias Matrix!(3,3,double) dmat3;   ///
903 alias Matrix!(3,4,double) dmat3x4; ///
904 alias Matrix!(3,0,double) dmat3xD; ///
905 alias Matrix!(4,2,double) dmat4x2; ///
906 alias Matrix!(4,3,double) dmat4x3; ///
907 alias Matrix!(4,4,double) dmat4;   ///
908 alias Matrix!(4,0,double) dmat4xD; ///
909 alias Matrix!(0,2,double) dmatDx2; ///
910 alias Matrix!(0,3,double) dmatDx3; ///
911 alias Matrix!(0,4,double) dmatDx4; ///
912 alias Matrix!(0,0,double) dmatD;   ///
913 
914 alias Matrix!(2,2,real) rmat2;   ///
915 alias Matrix!(2,3,real) rmat2x3; ///
916 alias Matrix!(2,4,real) rmat2x4; ///
917 alias Matrix!(2,0,real) rmat2xD; ///
918 alias Matrix!(3,2,real) rmat3x2; ///
919 alias Matrix!(3,3,real) rmat3;   ///
920 alias Matrix!(3,4,real) rmat3x4; ///
921 alias Matrix!(3,0,real) rmat3xD; ///
922 alias Matrix!(4,2,real) rmat4x2; ///
923 alias Matrix!(4,3,real) rmat4x3; ///
924 alias Matrix!(4,4,real) rmat4;   ///
925 alias Matrix!(4,0,real) rmat4xD; ///
926 alias Matrix!(0,2,real) rmatDx2; ///
927 alias Matrix!(0,3,real) rmatDx3; ///
928 alias Matrix!(0,4,real) rmatDx4; ///
929 alias Matrix!(0,0,real) rmatD;   ///
930 
931 unittest
932 {
933     static assert( Matrix!(3,3,float).sizeof == float.sizeof * 9 );
934     static assert( Matrix!(3,3,float).isStatic );
935     static assert( Matrix!(0,3,float).isDynamic );
936     static assert( Matrix!(0,3,float).isDynamicHeight );
937     static assert( Matrix!(0,3,float).isStaticWidth );
938     static assert( Matrix!(3,0,float).isDynamic );
939     static assert( Matrix!(3,0,float).isDynamicWidth );
940     static assert( Matrix!(3,0,float).isStaticHeight );
941     static assert( Matrix!(0,0,float).isDynamic );
942     static assert( Matrix!(0,0,float).isDynamicHeight );
943     static assert( Matrix!(0,0,float).isDynamicWidth );
944 }
945 
946 unittest
947 {
948     static assert( isStaticMatrix!(mat3) );
949     static assert( !isStaticMatrix!(matD) );
950     static assert( !isStaticMatrix!(int[]) );
951 }
952 
953 unittest
954 {
955     assert( eq( mat3.init, [[1,0,0],[0,1,0],[0,0,1]] ) );
956     assert( eq( mat2.init, [[1,0],[0,1]] ) );
957     assert( eq( mat2x3.init, [[0,0,0],[0,0,0]] ) );
958 }
959 
960 unittest
961 {
962     auto a = Matrix!(3,2,double)( 36, 0, 3, 3, 0, 0 );
963     auto b = Matrix!(3,2,double)( [ 36, 0, 3, 3, 0, 0 ] );
964     assert( eq( a.asArray, b.asArray ) );
965     assert( eq( a.asArray, [ 36, 0, 3, 3, 0, 0 ] ) );
966 }
967 
968 unittest
969 {
970     auto a = mat3( 1,2,3,4,5,6,7,8,9 );
971     assert( a.height == 3 );
972     assert( a.width == 3 );
973     assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) );
974     static assert( !__traits(compiles,a.resize(1,1)) );
975     a.fill(1);
976     assert( eq( a, [[1,1,1], [1,1,1], [1,1,1]] ) );
977     a[0][0] *= 3;
978     assert( eq( a, [[3,1,1], [1,1,1], [1,1,1]] ) );
979     a[1][0] += 3;
980     assert( eq( a, [[3,1,1], [4,1,1], [1,1,1]] ) );
981 }
982 
983 ///
984 unittest
985 {
986     static struct Test
987     {
988         union
989         {
990             mat3 um;
991             float[9] uf;
992         }
993     }
994 
995     Test tt;
996 
997     foreach( i, ref v; tt.uf ) v = i+1;
998     assert( eq( tt.um, [[1,2,3],[4,5,6],[7,8,9]] ) );
999 }
1000 
1001 ///
1002 unittest
1003 {
1004     auto a = matDx3( 1,2,3,4,5,6,7,8,9 );
1005     assert( mustExcept( {matDx3(1,2,3,4);} ) );
1006     assert( a.height == 3 );
1007     assert( a.width == 3 );
1008     assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) );
1009     assert( mustExcept({ a.resize(2,2); }) );
1010     a.resize(2,3);
1011     assert( eq( a, [[1,2,3],[4,5,6]] ) );
1012     assert( a.height == 2 );
1013     a.fill(1);
1014     assert( eq( a, [[1,1,1],[1,1,1]] ) );
1015     a.resize(0,3);
1016     assert( a.width == 3 );
1017     a.resize(2,3);
1018     a.fill(1);
1019 
1020     auto b = a;
1021     assert( eq( b, [[1,1,1],[1,1,1]] ) );
1022 }
1023 
1024 unittest
1025 {
1026     auto a = mat3xD( 1,2,3,4,5,6,7,8,9 );
1027     assert( mustExcept( {mat3xD(1,2,3,4);} ) );
1028     assert( a.height == 3 );
1029     assert( a.width == 3 );
1030     assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) );
1031     assert( mustExcept({ a.resize(2,2); }) );
1032     a.resize(3,2);
1033     assert( eq( a, [[1,2],[4,5],[7,8]] ) );
1034     assert( a.width == 2 );
1035     a.fill(1);
1036     assert( eq( a, [[1,1],[1,1],[1,1]] ) );
1037 
1038     auto b = a;
1039     assert( eq( b, [[1,1],[1,1],[1,1]] ) );
1040 }
1041 
1042 unittest
1043 {
1044     auto a = matD( 3,3, 1,2,3,4,5,6,7,8,9 );
1045     assert( mustExcept( {matD(1,2,3,4,5);} ) );
1046     assert( a.height == 3 );
1047     assert( a.width == 3 );
1048     assert( eq( a, [[1,2,3],[4,5,6],[7,8,9]] ) );
1049     a.resize(2,2);
1050     assert( eq( a, [[1,2],[4,5]] ) );
1051     auto b = matD(2,2);
1052     b.fill(1);
1053     assert( eq( b, [[1,1],[1,1]] ) );
1054     b = a;
1055     assert( eq( a, b ) );
1056     auto c = matD( Matrix!(2,4,float)(1,2,3,4,5,6,7,8) );
1057     assert( eq( c, [[1,2,3,4],[5,6,7,8]] ) );
1058     assert( c.height == 2 );
1059     assert( c.width == 4 );
1060     assert( b.height == 2 );
1061     assert( b.width == 2 );
1062     b = c;
1063     assert( b.height == 2 );
1064     assert( b.width == 4 );
1065     assert( eq( b, c ) );
1066     b[0][0] = 666;
1067     assert( !eq( b, c ) );
1068 }
1069 
1070 unittest
1071 {
1072     auto a = mat3xD( 1,2,3,4,5,6,7,8,9 );
1073     matD b;
1074     assert( b.height == 0 );
1075     assert( b.width == 0 );
1076     b = a;
1077     assert( b.height == 3 );
1078     assert( b.width == 3 );
1079     assert( eq( a, b ) );
1080     a[0][0] = 22;
1081     assert( !eq( a, b ) );
1082     a = b;
1083     assert( eq( a, b ) );
1084     b.height = 4;
1085     assert(  mustExcept({ a = b; }) );
1086     assert( !mustExcept({ b = a; }) );
1087 }
1088 
1089 ///
1090 unittest
1091 {
1092     auto a = mat3( 1,2,3,4,5,6,7,8,9 );
1093     auto b = matD( 3,3, 1,2,3,4,5,6,7,8,9 );
1094     auto c = mat3xD( 1,2,3,4,5,6,7,8,9 );
1095     auto d = matDx3( 1,2,3,4,5,6,7,8,9 );
1096 
1097     auto eha = a.expandHeight( 8,8,8 );
1098     auto ehb = b.expandHeight( 8,8,8 );
1099     auto ehc = c.expandHeight( 8,8,8 );
1100     auto ehd = d.expandHeight( 8,8,8 );
1101 
1102     assert( eq( eha, [[1,2,3],[4,5,6],[7,8,9],[8,8,8]] ));
1103     assert( eha.height == 4 );
1104     assert( ehb.height == 4 );
1105     assert( ehc.height == 4 );
1106     assert( ehd.height == 4 );
1107     assert( eq( eha, ehb ) );
1108     assert( eq( eha, ehc ) );
1109     assert( eq( eha, ehd ) );
1110 
1111     static assert( is(typeof(eha) == Matrix!(0,3,float)) );
1112     static assert( is(typeof(ehd) == Matrix!(0,3,float)) );
1113 
1114     static assert( is(typeof(ehb) == Matrix!(0,0,float)) );
1115     static assert( is(typeof(ehc) == Matrix!(0,0,float)) );
1116 
1117     auto ewa = a.expandWidth( 8,8,8 );
1118     auto ewb = b.expandWidth( 8,8,8 );
1119     auto ewc = c.expandWidth( 8,8,8 );
1120     auto ewd = d.expandWidth( 8,8,8 );
1121 
1122     assert( eq( ewa, [[1,2,3,8],[4,5,6,8],[7,8,9,8]] ));
1123     assert( ewa.width == 4 );
1124     assert( ewb.width == 4 );
1125     assert( ewc.width == 4 );
1126     assert( ewd.width == 4 );
1127     assert( eq( ewa, ewb ) );
1128     assert( eq( ewa, ewc ) );
1129     assert( eq( ewa, ewd ) );
1130 
1131     static assert( is(typeof(ewa) == Matrix!(3,0,float)) );
1132     static assert( is(typeof(ewc) == Matrix!(3,0,float)) );
1133 
1134     static assert( is(typeof(ewb) == Matrix!(0,0,float)) );
1135     static assert( is(typeof(ewd) == Matrix!(0,0,float)) );
1136 
1137     auto aa = a.expandHeight(a);
1138     assert( eq( aa, [[1,2,3],[4,5,6],[7,8,9],[1,2,3],[4,5,6],[7,8,9]] ));
1139     assert( aa.height == 6 );
1140     static assert( is(typeof(aa) == Matrix!(0,3,float)) );
1141 }
1142 
1143 unittest
1144 {
1145     auto a = mat3( 1,2,3,4,5,6,7,8,9 );
1146     assert( a.asArray == [1.0f,2,3,4,5,6,7,8,9] );
1147 }
1148 
1149 ///
1150 unittest
1151 {
1152     auto a = matD(4,4,0).fillDiag(1);
1153     assert( eq( a, mat4() ) );
1154     assert( eq( a.inv, a ) );
1155 }
1156 
1157 ///
1158 unittest
1159 {
1160     auto a = mat4x2( 1,2,3,4,5,6,7,8 );
1161     auto b = mat4( 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 );
1162     auto c = a.T * b * a;
1163     static assert( c.height == 2 && c.width == 2 );
1164 }
1165 
1166 unittest
1167 {
1168     matD a;
1169     matD b;
1170     a.resize( 10, 4 );
1171     b.resize( 10, 10 );
1172     auto c = a.T * b * a;
1173     assert( c.height == 4 && c.width == 4 );
1174 }
1175 
1176 ///
1177 unittest
1178 {
1179     auto a = mat3.diag(1);
1180     assert( eq(a,[[1,0,0],[0,1,0],[0,0,1]]) );
1181     auto b = mat3xD.diag(1,2);
1182     assert( eq(b,[[1,0,0],[0,2,0],[0,0,1]]) );
1183     auto c = mat3xD.diag(1,2,3);
1184     assert( eq(c,[[1,0,0],[0,2,0],[0,0,3]]) );
1185     static assert( !__traits(compiles,matD.diag(1)) );
1186     auto d = matD(3,3).fill(0).fillDiag(1);
1187     assert( eq(d,[[1,0,0],[0,1,0],[0,0,1]]) );
1188 }
1189 
1190 ///
1191 unittest
1192 {
1193     auto a = mat3( 1,2,3,4,5,6,7,8,9 );
1194     auto sha = a.sliceHeight(1);
1195     assert( eq(sha,[[4,5,6],[7,8,9]]) );
1196     auto swsha = sha.sliceWidth(0,1);
1197     assert( eq(swsha,[[4],[7]]) );
1198 }
1199 
1200 ///
1201 unittest
1202 {
1203     auto a = mat3.diag(1);
1204     assert( eq( -a,[[-1,0,0],[0,-1,0],[0,0,-1]]) );
1205 }
1206 
1207 unittest
1208 {
1209     auto a = mat3.diag(1);
1210     auto b = a*3-a;
1211     assert( eq( b,a*2 ) );
1212     b /= 2;
1213     assert( eq( b,a ) );
1214 }
1215 
1216 ///
1217 unittest
1218 {
1219     auto a = rmat3( 3,2,2, 1,3,1, 5,3,4 );
1220     auto ainv = rmat3xD( 9,-2,-4,  1,2,-1, -12,1,7 ) / 5;
1221 
1222     auto b = a * ainv;
1223     static assert( is( typeof(b) == Matrix!(3,0,real) ) );
1224     assert( eq( b, mat3.diag(1) ) );
1225 
1226     static assert( !__traits(compiles,a *= ainv ) );
1227     a *= rmat3(ainv);
1228     assert( eq( a, mat3.diag(1) ) );
1229 }
1230 
1231 ///
1232 unittest
1233 {
1234     alias Matrix!(4,5,float) mat4x5;
1235 
1236     auto a = mat2x4.init * mat4xD.init;
1237     assert( mustExcept({ mat4xD.init * mat2x4.init; }) );
1238     auto b = mat2x4.init * mat4x5.init;
1239 
1240     static assert( is( typeof(a) == Matrix!(2,0,float) ) );
1241     static assert( is( typeof(b) == Matrix!(2,5,float) ) );
1242     static assert( is( typeof(mat4xD.init * mat2x4.init) == Matrix!(4,4,float) ) );
1243 }
1244 
1245 unittest
1246 {
1247 
1248     auto a = mat2x3( 1,2,3, 4,5,6 );
1249     auto b = mat4xD( 1,2,3,4 );
1250 
1251     auto aT = a.T;
1252     auto bT = b.T;
1253 
1254     static assert( is( typeof(aT) == Matrix!(3,2,float) ) );
1255     static assert( is( typeof(bT) == Matrix!(0,4,float) ) );
1256 
1257     assert( bT.height == b.width );
1258 }
1259 
1260 unittest
1261 {
1262     auto a = mat3.diag(1);
1263 
1264     a.setRow(2,4,5,6);
1265     assert( eq( a, [[1,0,0],[0,1,0],[4,5,6]] ) );
1266     a.setCol(1,8,4,2);
1267     assert( eq( a, [[1,8,0],[0,4,0],[4,2,6]] ) );
1268 
1269     assert( eq( a.row(0), [[1,8,0]] ) );
1270     assert( eq( a.col(0), [[1],[0],[4]] ) );
1271 }
1272 
1273 ///
1274 unittest
1275 {
1276     auto b = mat3.diag(2) * vec3(2,3,4);
1277     assert( is( typeof(b) == vec3 ) );
1278     assert( eq( b, [4,6,8] ) );
1279 
1280     auto c = vec3(1,1,1) * mat3.diag(1,2,3);
1281     assert( is( typeof(c) == vec3 ) );
1282     assert( eq( c, [1,2,3] ) );
1283 }
1284 
1285 ///
1286 unittest
1287 {
1288     auto mtr = matD(2,3).fill( 1,2,3, 
1289                                3,4,7 );
1290 
1291     auto a = mtr * vec3(1,1,1);
1292 
1293     assert( is( typeof(a) == Vector!(0,float) ) );
1294     assert( a.length == 2 );
1295     assert( eq( a, [ 6, 14 ] ) );
1296 
1297     auto b = vec3(1,1,1) * mtr.T;
1298     assert( eq( a, b ) );
1299 }
1300 
1301 ///
1302 unittest
1303 {
1304     void check(E)( E mtr ) if( isMatrix!E )
1305     {
1306         mtr.fill( 1,2,3,4,
1307                   5,6,7,8,
1308                   9,10,11,12,
1309                   13,14,15,16 );
1310         auto sm = mtr.subWithout( [0,3,3,3], [1,2] );
1311         assert( is( typeof(sm) == matD ) );
1312         assert( sm.width == 2 );
1313         assert( sm.height == 2 );
1314         assert( eq( sm, [ [5,8], [9,12] ] ) );
1315         assert( mustExcept({ mtr.sub( [0,4], [1,2] ); }) );
1316         auto sm2 = mtr.subWithout( [], [1,2] );
1317         assert( sm2.width == 2 );
1318         assert( sm2.height == 4 );
1319         assert( eq( sm2, [ [1,4],[5,8],[9,12],[13,16] ] ) );
1320     }
1321 
1322     check( matD(4,4) );
1323     check( mat4() );
1324 }
1325 
1326 unittest
1327 {
1328     assert( eq( matD(4,4,0).fillDiag(1,2,3,4).det, 24 ) );
1329 }
1330 
1331 unittest
1332 {
1333     auto mtr = rmatD(4,4).fill( 1,2,5,2,
1334                                 5,6,1,4,
1335                                 9,1,3,0,
1336                                 9,2,4,2 );
1337     auto xx = mtr * mtr.inv;
1338     assert( eq( xx, matD(4,4,0).fillDiag(1) ) );
1339 }
1340 
1341 unittest
1342 {
1343     auto mtr = matD(4,4).fill( 0,1,0,2,
1344                                1,0,0,4,
1345                                0,0,1,1,
1346                                0,0,0,1 );
1347     auto vv = vec4(4,2,1,1);
1348     auto rv = mtr.speedTransformInv * (mtr*vv);
1349     assert( eq( rv, vv ) );
1350 }
1351 
1352 unittest
1353 {
1354     auto mtr = matD(4,4).fillDiag(1);
1355     auto vv = vec4(4,2,1,1);
1356     auto rv = vv * mtr;
1357     auto vr = mtr.T * vv * mtr;
1358 }
1359 
1360 unittest
1361 {
1362     auto mtr = rmat4.diag(1);
1363     auto vv = vec4(4,2,1,1);
1364     auto rv = vv * mtr;
1365     auto vr = mtr.T * vv * mtr;
1366 
1367     auto xx = mtr * mtr.inv;
1368     assert( eq( xx, mat4.diag(1) ) );
1369 }
1370 
1371 unittest
1372 {
1373     auto mtr = rmat4( 1,2,5,2,
1374                       5,6,1,4,
1375                       9,1,3,0,
1376                       9,2,4,2 );
1377 
1378     auto xx = mtr * mtr.rowReduceInv;
1379     assert( eq( xx, mat4() ) );
1380 }
1381 
1382 ///
1383 unittest
1384 {
1385     auto mtr = mat4().setRect(0,0,mat3.diag(1)).setCol(3,1,2,3,4);
1386     assert( eq( mtr, [[1,0,0,1],
1387                       [0,1,0,2],
1388                       [0,0,1,3],
1389                       [0,0,0,4]] ) );
1390 }
1391 
1392 unittest
1393 {
1394     auto stm = mat4();
1395     assert( stm );
1396     auto dnm = matD();
1397     assert( dnm );
1398 }
1399 
1400 ///
1401 auto quatToMatrix(E)( in Quaterni!E iq )
1402 {
1403     auto q = iq / iq.len2;
1404 
1405     E wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
1406 
1407     x2 = q.i + q.i;
1408     y2 = q.j + q.j;
1409     z2 = q.k + q.k;
1410     xx = q.i * x2;   xy = q.i * y2;   xz = q.i * z2;
1411     yy = q.j * y2;   yz = q.j * z2;   zz = q.k * z2;
1412     wx = q.a * x2;   wy = q.a * y2;   wz = q.a * z2;
1413 
1414     return Matrix!(3,3,E)( 1.0-(yy+zz),  xy-wz,        xz+wy,
1415                            xy+wz,        1.0-(xx+zz),  yz-wx,
1416                            xz-wy,        yz+wx,        1.0-(xx+yy) );
1417 }
1418 
1419 ///
1420 unittest
1421 {
1422     auto q = rquat.fromAngle( PI_2, vec3(0,0,1) );
1423 
1424     auto m = quatToMatrix(q);
1425     auto r = mat3(0,-1, 0, 1, 0, 0, 0, 0, 1);
1426     assert( eq_approx( m, r, 1e-7 ) );
1427 }
1428 
1429 ///
1430 auto quatAndPosToMatrix(E,V)( in Quaterni!E iq, in V pos )
1431     if( isCompatibleVector!(3,E,V) )
1432 {
1433     auto q = iq / iq.len2;
1434 
1435     E wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
1436 
1437     x2 = q.i + q.i;
1438     y2 = q.j + q.j;
1439     z2 = q.k + q.k;
1440     xx = q.i * x2;   xy = q.i * y2;   xz = q.i * z2;
1441     yy = q.j * y2;   yz = q.j * z2;   zz = q.k * z2;
1442     wx = q.a * x2;   wy = q.a * y2;   wz = q.a * z2;
1443 
1444     return Matrix!(4,4,E)( 1.0-(yy+zz),  xy-wz,        xz+wy,       pos.x,
1445                            xy+wz,        1.0-(xx+zz),  yz-wx,       pos.y,
1446                            xz-wy,        yz+wx,        1.0-(xx+yy), pos.z,
1447                            0,            0,            0,           1     );
1448 }
1449 
1450 ///
1451 unittest
1452 {
1453     auto q = rquat.fromAngle( PI_2, vec3(0,0,1) );
1454     auto m = quatAndPosToMatrix(q, vec3(1,2,3) );
1455     assert( eq_approx( m, [[0,-1,0,1],
1456                            [1, 0,0,2],
1457                            [0, 0,1,3],
1458                            [0, 0,0,1]], 1e-7 ) );
1459 
1460 }