File Coverage

blib/lib/Math/Zap/Matrix.pm
Criterion Covered Total %
statement 101 112 90.1
branch 25 38 65.7
condition 8 24 33.3
subroutine 31 35 88.5
pod 23 30 76.6
total 188 239 78.6


line stmt bran cond sub pod time code
1            
2             =head1 Matrix
3            
4             3*3 matrix manipulation
5            
6             PhilipRBrenan@yahoo.com, 2004, Perl License
7            
8            
9             =head2 Synopsis
10            
11             Example t/matrix.t
12            
13             #_ Matrix _____________________________________________________________
14             # Test 3*3 matrices
15             # philiprbrenan@yahoo.com, 2004, Perl License
16             #______________________________________________________________________
17            
18             use Math::Zap::Matrix identity=>i;
19             use Math::Zap::Vector;
20             use Test::Simple tests=>8;
21            
22             my ($a, $b, $c, $v);
23            
24             $a = matrix
25             (8, 0, 0,
26             0, 8, 0,
27             0, 0, 8
28             );
29            
30             $b = matrix
31             (4, 2, 0,
32             2, 4, 2,
33             0, 2, 4
34             );
35            
36             $c = matrix
37             (4, 2, 1,
38             2, 4, 2,
39             1, 2, 4
40             );
41            
42             $v = vector(1,2,3);
43            
44             ok($a/$a == i());
45             ok($b/$b == i());
46             ok($c/$c == i());
47             ok(2/$a*$a/2 == i());
48             ok(($a+$b)/($a+$b) == i());
49             ok(($a-$c)/($a-$c) == i());
50             ok(-$a/-$a == i());
51             ok(1/$a*($a*$v) == $v);
52            
53            
54            
55            
56             =head2 Description
57            
58             3*3 matrix manipulation
59            
60             =cut
61            
62            
63             package Math::Zap::Matrix;
64             $VERSION=1.07;
65 5     5   1510 use Math::Zap::Vector check=>'vectorCheck', is=>'vectorIs';
  5         8  
  5         168  
66 5     5   25 use Carp;
  5         9  
  5         323  
67 5     5   25 use constant debug => 0; # Debugging level
  5         8  
  5         13680  
68            
69            
70             =head2 Constructors
71            
72            
73             =head3 new
74            
75             Create a matrix
76            
77             =cut
78            
79            
80 3     3 1 5 sub new($$$$$$$$$)
81             {my
82             ($a11, $a12, $a13,
83             $a21, $a22, $a23,
84             $a31, $a32, $a33,
85             ) = @_;
86            
87 3         23 my $m = round(bless(
88             {11=>$a11, 12=>$a12, 13=>$a13,
89             21=>$a21, 22=>$a22, 23=>$a23,
90             31=>$a31, 32=>$a32, 33=>$a33,
91             }));
92 3         9 singular($m, 1);
93 3         8 $m;
94             }
95            
96            
97             =head3 matrix
98            
99             Create a matrix = synonym for L
100            
101             =cut
102            
103            
104 3     3 1 10 sub matrix($$$$$$$$$)
105             {new($_[0],$_[1],$_[2],$_[3],$_[4],$_[5],$_[6],$_[7],$_[8]);
106             }
107            
108            
109             =head3 new3v
110            
111             Create a matrix from three vectors
112            
113             =cut
114            
115            
116 36     36 1 57 sub new3v($$$)
117             {my ($a, $b, $c) = @_;
118 36         43 vectorCheck(@_) if debug;
119 36         138 my $m = round(bless(
120             {11=>$a->x, 12=>$b->x, 13=>$c->x,
121             21=>$a->y, 22=>$b->y, 23=>$c->y,
122             31=>$a->z, 32=>$b->z, 33=>$c->z,
123             }));
124 36         100 singular($m, 1);
125 36         176 $m;
126             }
127            
128            
129             =head3 new3vnc
130            
131             Create a matrix from three vectors without checking
132            
133             =cut
134            
135            
136 0     0 1 0 sub new3vnc($$$)
137             {my ($a, $b, $c) = vectorCheck(@_);
138 0         0 my $m = round(bless(
139             {11=>$a->x, 12=>$b->x, 13=>$c->x,
140             21=>$a->y, 22=>$b->y, 23=>$c->y,
141             31=>$a->z, 32=>$b->z, 33=>$c->z,
142             }));
143 0         0 $m;
144             }
145            
146            
147             =head2 Methods
148            
149            
150             =head3 check
151            
152             Check its a matrix
153            
154             =cut
155            
156            
157             sub check(@)
158 65     65 1 62 {if (debug)
159             {for my $m(@_)
160             {confess "$m is not a matrix" unless ref($m) eq __PACKAGE__;
161             }
162             }
163 65         114 return (@_)
164             }
165            
166            
167             =head3 is
168            
169             Test its a matrix
170            
171             =cut
172            
173            
174 7 50       21 sub is(@)
175 7     7 1 9 {for my $m(@_)
176             {return 0 unless ref($m) eq __PACKAGE__;
177             }
178 7         31 'matrix';
179             }
180            
181            
182             =head3 singular
183            
184             Singular matrix?
185            
186             =cut
187            
188            
189 39     39 1 54 sub singular($$)
190             {my $m = shift; # Matrix
191 39         49 my $a = 1e-2; # Accuracy
192 39         50 my $A = shift; # Action 0: return indicator, 1: confess
193            
194 39         243 my $n = abs
195             ($m->{11}*$m->{22}*$m->{33}
196             -$m->{11}*$m->{23}*$m->{32}
197             -$m->{12}*$m->{21}*$m->{33}
198             +$m->{12}*$m->{23}*$m->{31}
199             +$m->{13}*$m->{21}*$m->{32}
200             -$m->{13}*$m->{22}*$m->{31})
201             < $a;
202 39 50 33     109 confess "Singular matrix2" if $n and $A;
203 39         69 $n;
204             }
205            
206            
207             =head3 accuracy
208            
209             Get/Set accuracy for comparisons
210            
211             =cut
212            
213            
214             my $accuracy = 1e-10;
215            
216             sub accuracy
217 0 0   0 1 0 {return $accuracy unless scalar(@_);
218 0         0 $accuracy = shift();
219             }
220            
221            
222             =head3 round
223            
224             Round: round to nearest integer if within accuracy of that integer
225            
226             =cut
227            
228            
229 97     97 1 130 sub round($)
230             {my ($a) = @_;
231 97         108 check(@_) if debug;
232 97         98 my ($n, $N);
233 97         150 for my $k(qw(11 12 13 21 22 23 31 32 33))
  873         1467  
234             {$n = $a->{$k};
235 873         829 $N = int($n);
236 873 100       2236 $a->{$k} = $N if abs($n-$N) < $accuracy;
237             }
238 97         584 $a;
239             }
240            
241            
242             =head3 clone
243            
244             Create a matrix from another matrix
245            
246             =cut
247            
248            
249 0     0 1 0 sub clone($)
250             {my ($m) = check(@_); # Matrix
251 0         0 round bless
252             {11=>$m->{11}, 12=>$m->{12}, 13=>$m->{13},
253             21=>$m->{21}, 22=>$m->{22}, 23=>$m->{23},
254             31=>$m->{31}, 32=>$m->{32}, 33=>$m->{33},
255             };
256             }
257            
258            
259             =head3 print
260            
261             Print matrix
262            
263             =cut
264            
265            
266 4     4 1 7 sub print($)
267             {my ($m) = check(@_); # Matrix
268 4         26 'matrix('.$m->{11}.', '.$m->{12}.', '.$m->{13}.
269             ', '.$m->{21}.', '.$m->{22}.', '.$m->{23}.
270             ', '.$m->{31}.', '.$m->{32}.', '.$m->{33}.
271             ')';
272             }
273            
274            
275             =head3 add
276            
277             Add matrices
278            
279             =cut
280            
281            
282 2     2 1 5 sub add($$)
283             {my ($a, $b) = check(@_); # Matrices
284 2         21 round bless
285             {11=>$a->{11}+$b->{11}, 12=>$a->{12}+$b->{12}, 13=>$a->{13}+$b->{13},
286             21=>$a->{21}+$b->{21}, 22=>$a->{22}+$b->{22}, 23=>$a->{23}+$b->{23},
287             31=>$a->{31}+$b->{31}, 32=>$a->{32}+$b->{32}, 33=>$a->{33}+$b->{33},
288             };
289             }
290            
291            
292             =head3 negate
293            
294             Negate matrix
295            
296             =cut
297            
298            
299 2     2 1 5 sub negate($)
300             {my ($a) = check(@_); # Matrices
301 2         19 round bless
302             {11=>-$a->{11}, 12=>-$a->{12}, 13=>-$a->{13},
303             21=>-$a->{21}, 22=>-$a->{22}, 23=>-$a->{23},
304             31=>-$a->{31}, 32=>-$a->{32}, 33=>-$a->{33},
305             };
306             }
307            
308            
309             =head3 subtract
310            
311             Subtract matrices
312            
313             =cut
314            
315            
316 2     2 1 49 sub subtract($$)
317             {my ($a, $b) = check(@_); # Matrices
318 2         21 round bless
319             {11=>$a->{11}-$b->{11}, 12=>$a->{12}-$b->{12}, 13=>$a->{13}-$b->{13},
320             21=>$a->{21}-$b->{21}, 22=>$a->{22}-$b->{22}, 23=>$a->{23}-$b->{23},
321             31=>$a->{31}-$b->{31}, 32=>$a->{32}-$b->{32}, 33=>$a->{33}-$b->{33},
322             };
323             }
324            
325            
326             =head3 matrixVectorMultiply
327            
328             Vector = Matrix * Vector
329            
330             =cut
331            
332            
333 38     38 1 89 sub matrixVectorMultiply($$)
334             {my ($a) = check(@_[0..0]); # Matrix
335 38         1243 my ($b) = vectorCheck(@_[1..1]); # Vector
336 38         134 vector
337             ($a->{11}*$b->x+$a->{12}*$b->y+$a->{13}*$b->z,
338             $a->{21}*$b->x+$a->{22}*$b->y+$a->{23}*$b->z,
339             $a->{31}*$b->x+$a->{32}*$b->y+$a->{33}*$b->z,
340             );
341             }
342            
343            
344             =head3 matrixScalarMultiply
345            
346             Matrix = Matrix * scalar
347            
348             =cut
349            
350            
351 2     2 1 6 sub matrixScalarMultiply($$)
352             {my ($a) = check(@_[0..0]); # Matrix
353 2         4 my ($b) = @_[1..1]; # Scalar
354 2 50       6 confess "$b is not a scalar" if ref($b);
355 2         21 round bless
356             {11=>$a->{11}*$b, 12=>$a->{12}*$b, 13=>$a->{13}*$b,
357             21=>$a->{21}*$b, 22=>$a->{22}*$b, 23=>$a->{23}*$b,
358             31=>$a->{31}*$b, 32=>$a->{32}*$b, 33=>$a->{33}*$b,
359             };
360             }
361            
362            
363             =head3 matrixMatrixMultiply
364            
365             Matrix = Matrix * Matrix
366            
367             =cut
368            
369            
370 7     7 1 30 sub matrixMatrixMultiply($$)
371             {my ($a, $b) = check(@_); # Matrices
372 7         145 round bless
373             {11=>$a->{11}*$b->{11}+$a->{12}*$b->{21}+$a->{13}*$b->{31}, 12=>$a->{11}*$b->{12}+$a->{12}*$b->{22}+$a->{13}*$b->{32}, 13=>$a->{11}*$b->{13}+$a->{12}*$b->{23}+$a->{13}*$b->{33},
374             21=>$a->{21}*$b->{11}+$a->{22}*$b->{21}+$a->{23}*$b->{31}, 22=>$a->{21}*$b->{12}+$a->{22}*$b->{22}+$a->{23}*$b->{32}, 23=>$a->{21}*$b->{13}+$a->{22}*$b->{23}+$a->{23}*$b->{33},
375             31=>$a->{31}*$b->{11}+$a->{32}*$b->{21}+$a->{33}*$b->{31}, 32=>$a->{31}*$b->{12}+$a->{32}*$b->{22}+$a->{33}*$b->{32}, 33=>$a->{31}*$b->{13}+$a->{32}*$b->{23}+$a->{33}*$b->{33},
376             };
377             }
378            
379            
380             =head3 matrixScalarDivide
381            
382             Matrix=Matrix / non zero scalar
383            
384             =cut
385            
386            
387 1     1 1 31 sub matrixScalarDivide($$)
388             {my ($a) = check(@_[0..0]); # Matrices
389 1         3 my ($b) = @_[1..1]; # Scalar
390 1 50       4 confess "$b is not a scalar" if ref($b);
391 1 50       3 confess "$b is zero" if $b == 0;
392 1         12 round bless
393             {11=>$a->{11}/$b, 12=>$a->{12}/$b, 13=>$a->{13}/$b,
394             21=>$a->{21}/$b, 22=>$a->{22}/$b, 23=>$a->{23}/$b,
395             31=>$a->{31}/$b, 32=>$a->{32}/$b, 33=>$a->{33}/$b,
396             };
397             }
398            
399            
400             =head3 det
401            
402             Determinant of matrix.
403            
404             =cut
405            
406            
407 42     42 1 49 sub det($)
408             {my ($a) = @_; # Matrix
409 42         40 check(@_) if debug; # Check
410            
411 42         245 +$a->{11}*$a->{22}*$a->{33}
412             -$a->{11}*$a->{23}*$a->{32}
413             -$a->{12}*$a->{21}*$a->{33}
414             +$a->{12}*$a->{23}*$a->{31}
415             +$a->{13}*$a->{21}*$a->{32}
416             -$a->{13}*$a->{22}*$a->{31};
417             }
418            
419            
420             =head3 d2
421            
422             Determinant of 2*2 matrix
423            
424             =cut
425            
426            
427 378     378 1 473 sub d2($$$$)
428             {my ($a, $b, $c, $d) = @_;
429 378         1294 $a*$d-$b*$c;
430             }
431            
432            
433             =head3 inverse
434            
435             Inverse of matrix
436            
437             =cut
438            
439            
440 44     44 1 57 sub inverse($)
441             {my ($a) = @_; # Matrix
442 44         40 check(@_) if debug; # Check
443 44 100       115 return $a->{inverse} if defined($a->{inverse});
444            
445 42         89 my $d = det($a);
446 42 50       93 return undef if $d == 0;
447            
448 42         123 my $i = round bless
449             {11=>d2($a->{22}, $a->{32}, $a->{23}, $a->{33})/$d,
450             21=>d2($a->{23}, $a->{33}, $a->{21}, $a->{31})/$d,
451             31=>d2($a->{21}, $a->{31}, $a->{22}, $a->{32})/$d,
452            
453             12=>d2($a->{13}, $a->{33}, $a->{12}, $a->{32})/$d,
454             22=>d2($a->{11}, $a->{31}, $a->{13}, $a->{33})/$d,
455             32=>d2($a->{12}, $a->{32}, $a->{11}, $a->{31})/$d,
456            
457             13=>d2($a->{12}, $a->{22}, $a->{13}, $a->{23})/$d,
458             23=>d2($a->{13}, $a->{23}, $a->{11}, $a->{21})/$d,
459             33=>d2($a->{11}, $a->{21}, $a->{12}, $a->{22})/$d,
460             };
461 42         124 $a->{inverse} = $i;
462 42         119 $i;
463             }
464            
465            
466             =head3 identity
467            
468             Identity matrix
469            
470             =cut
471            
472            
473 7     7 1 124 sub identity()
474             {bless
475             {11=>1, 21=>0, 31=>0,
476             12=>0, 22=>1, 32=>0,
477             13=>0, 23=>0, 33=>1,
478             };
479             }
480            
481            
482             =head3 equals
483            
484             Equals to within accuracy
485            
486             =cut
487            
488            
489 7     7 1 12 sub equals($$)
490             {my ($a, $b) = check(@_); # Matrices
491 7 50 33     191 abs($a->{11}-$b->{11}) < $accuracy and
      33        
      33        
      33        
      33        
      33        
      33        
492             abs($a->{12}-$b->{12}) < $accuracy and
493             abs($a->{13}-$b->{13}) < $accuracy and
494            
495             abs($a->{21}-$b->{21}) < $accuracy and
496             abs($a->{22}-$b->{22}) < $accuracy and
497             abs($a->{23}-$b->{23}) < $accuracy and
498            
499             abs($a->{31}-$b->{31}) < $accuracy and
500             abs($a->{32}-$b->{32}) < $accuracy and
501             abs($a->{33}-$b->{33}) < $accuracy;
502             }
503            
504            
505             =head3 Operator
506            
507             Operator overloads
508            
509             =cut
510            
511            
512             use overload
513 5         86 '+' => \&add3, # Add two vectors
514             '-' => \&subtract3, # Subtract one vector from another
515             '*' => \&multiply3, # Times by a scalar, or vector dot product
516             '/' => \÷3, # Divide by a scalar
517             '!' => \&det3, # Determinant
518             '==' => \&equals3, # Equals (to accuracy)
519             '""' => \&print3, # Print
520 5     5   38 'fallback' => FALSE;
  5         16  
521            
522            
523             =head3 Add operator
524            
525             Add operator.
526            
527             =cut
528            
529            
530             sub add3
531 2     2 0 3 {my ($a, $b) = @_;
532 2         6 $a->add($b);
533             }
534            
535            
536             =head3 subtract operator
537            
538             Negate operator.
539            
540             =cut
541            
542            
543             sub subtract3
544 4     4 0 8 {my ($a, $b, $c) = @_;
545            
546 4 100       9 return $a->subtract($b) if $b;
547 2         6 negate($a);
548             }
549            
550            
551             =head3 multiply operator
552            
553             Multiply operator.
554            
555             =cut
556            
557            
558             sub multiply3
559 3     3 0 5 {my ($a, $b) = @_;
560 3 50       8 return $a->matrixScalarMultiply($b) unless ref($b);
561 3 100       85 return $a->matrixVectorMultiply($b) if vectorIs($b);
562 1 50       4 return $a->matrixMatrixMultiply($b) if is($b);
563 0         0 confess "Cannot multiply $a by $b\n";
564             }
565            
566            
567             =head3 divide operator
568            
569             Divide operator.
570            
571             =cut
572            
573            
574             sub divide3
575 45     45 0 83 {my ($a, $b, $c) = @_;
576 45 100       111 if (!ref($b))
  3 100       9  
577 42 100       1260 {return $a->matrixScalarDivide($b) unless $c;
578 2 50       8 return $a->inverse->matrixScalarMultiply($b) if $c;
579             }
580             else
581             {return $a->inverse->matrixVectorMultiply($b) if vectorIs($b);
582 6 50       12 return $a->matrixMatrixMultiply($b->inverse) if is($b);
583 0         0 confess "Cannot multiply $a by $b\n";
584             }
585             }
586            
587            
588             =head3 equals operator
589            
590             Equals operator.
591            
592             =cut
593            
594            
595             sub equals3
596 7     7 0 12 {my ($a, $b, $c) = @_;
597 7         13 return $a->equals($b);
598             }
599            
600            
601             =head3 det operator
602            
603             Determinant of a matrix
604            
605             =cut
606            
607            
608             sub det3
609 0     0 0 0 {my ($a, $b, $c) = @_;
610 0         0 $a->det;
611             }
612            
613            
614             =head3 print vector
615            
616             Print a vector.
617            
618             =cut
619            
620            
621             sub print3
622 4     4 0 5 {my ($a) = @_;
623 4         8 return $a->print;
624             }
625            
626            
627             =head2 Exports
628            
629             Export L, L, L, L
630            
631             =cut
632            
633            
634 5         37 use Math::Zap::Exports qw(
635             matrix ($$$$$$$$$)
636             identity ()
637             new3v ($$$)
638             new3vnc ($$$)
639 5     5   2587 );
  5         9  
640            
641             #_ Matrix _____________________________________________________________
642             # Package loaded successfully
643             #______________________________________________________________________
644            
645             1;
646            
647            
648             =head2 Credits
649            
650             =head3 Author
651            
652             philiprbrenan@yahoo.com
653            
654             =head3 Copyright
655            
656             philiprbrenan@yahoo.com, 2004
657            
658             =head3 License
659            
660             Perl License.
661            
662            
663             =cut