
=head1 Matrix2


2*2 matrix manipulation    

PhilipRBrenan@yahoo.com, 2004, Perl License


=head2 Synopsis


#example t/matrix2.t


=head2 Description


2*2 matrix manipulation    

 
 package matrix2;
 use vector2 check=>'vector2Check', is=>'vector2Is';
 use Carp;
 use constant debug => 0; # Debugging level
 


=head2 Constructors



=head3 new 


Create a matrix

 
 sub new($$$$)
  {my
   ($a11, $a12,
    $a21, $a22,
   ) = @_;
 
   my $m = round(bless(
    {11=>$a11, 12=>$a12,
     21=>$a21, 22=>$a22,
    }));
   singular($m, 1);
   $m;
  }
 


=head3 matrix2


Create a matrix. Synonym for L</new>.

 
 sub matrix2($$$$)
  {new($_[0],$_[1],$_[2],$_[3]);
  }
 


=head3 identity


Identity matrix

 
 sub identity()
  {bless
    {11=>1, 21=>0,                              
     12=>0, 22=>1,                              
    }; 
  }
 


=head3 new2v


Create a matrix from two vectors

 
 sub new2v($$)
  {vector2Check(@_) if debug;
   my ($a, $b, $c) =  @_;
   my $m = round(bless(
    {11=>$a->{x}, 12=>$b->{x},
     21=>$a->{y}, 22=>$b->{y},
    }));
   singular($m, 1);
   $m;
  }
 


=head2 Methods



=head3 check


Check its a matrix

 
 sub check(@)
  {if (debug)
    {for my $m(@_)
      {confess "$m is not a matrix2" unless ref($m) eq __PACKAGE__;
      } 
    }
   return (@_)
  }
 


=head3 is


Test its a matrix

 
 sub is(@)
  {for my $m(@_)
    {return 0 unless ref($m) eq __PACKAGE__;
    }
   'matrix2';
  }
 


=head3 accuracy


Get/Set accuracy 

 
 my $accuracy = 1e-10;
 
 sub accuracy
  {return $accuracy unless scalar(@_);
   $accuracy = shift();
  }
 


=head3 round


Round: round to nearest integer if within accuracy of that integer 

 
 sub round($)
  {unless (debug)
    {return $_[0];
    }
   else
    {my ($a) = @_;
     for my $k(keys(%$a))
      {my $n = $a->{$k};
       my $N = int($n);
       $a->{$k} = $N if abs($n-$N) < $accuracy;
      }
     return $a;
    }
  }
 


=head3 singular


Singular matrix?

 
 sub singular($$)
  {my $m = shift;  # Matrix   
   my $a = 1e-2;   # Accuracy
   my $A = shift;  # Action 0: return indicator, 1: confess 
   my $n = abs
     ($m->{11} * $m->{22} -                                        
      $m->{12} * $m->{21})
     < $a;
   confess "Singular matrix2" if $n and $A;
   $n;      
  }
 


=head3 clone


Create a matrix from another matrix

 
 sub clone($)
  {my ($m) = check(@_); # Matrix
   round bless
    {11=>$m->{11}, 12=>$m->{12},
     21=>$m->{21}, 22=>$m->{22},
    }; 
  }
 


=head3 print


Print matrix

 
 sub print($)
  {my ($m) = check(@_); # Matrix 
   'matrix2('.$m->{11}.', '.$m->{12}. 
         ', '.$m->{21}.', '.$m->{22}.
   ')';
  } 
 


=head3 add


Add matrices

 
 sub add($$)
  {my ($a, $b) = check(@_); # Matrices
   my $m = round bless
    {11=>$a->{11}+$b->{11}, 12=>$a->{12}+$b->{12}, 
     21=>$a->{21}+$b->{21}, 22=>$a->{22}+$b->{22}, 
    }; 
   singular($m, 1);
   $m;
  }
 


=head3 negate


Negate matrix

 
 sub negate($)
  {my ($a) = check(@_); # Matrices
   my $m = round bless
    {11=>-$a->{11}, 12=>-$a->{12},
     21=>-$a->{21}, 22=>-$a->{22},
    }; 
   singular($m, 1);
   $m;
  }
 


=head3 subtract


Subtract matrices

 
 sub subtract($$)
  {my ($a, $b) = check(@_); # Matrices
   my $m = round bless
    {11=>$a->{11}-$b->{11}, 12=>$a->{12}-$b->{12},
     21=>$a->{21}-$b->{21}, 22=>$a->{22}-$b->{22},
    }; 
   singular($m, 1);
   $m;
  }
 


=head3 matrixVectorMultiply


Vector = Matrix * Vector     

 
 sub matrixVectorMultiply($$)
  {       check(@_[0..0]) if debug; # Matrix
   vector2Check(@_[1..1]) if debug; # Vector 
   my ($a, $b) = @_;
   vector2
    ($a->{11}*$b->{x}+$a->{12}*$b->{y},
     $a->{21}*$b->{x}+$a->{22}*$b->{y},
    );
  }
 


=head3 matrixScalarMultiply


Matrix = Matrix * scalar      

 
 sub matrixScalarMultiply($$)
  {my ($a) = check(@_[0..0]); # Matrix
   my ($b) = @_[1..1];        # Scalar
   confess "$b is not a scalar" if ref($b);   
   round bless
    {11=>$a->{11}*$b, 12=>$a->{12}*$b,
     21=>$a->{21}*$b, 22=>$a->{22}*$b,
    }; 
  }
 


=head3 matrixMatrixMultiply


Matrix = Matrix * Matrix      

 
 sub matrixMatrixMultiply($$)
  {my ($a, $b) = check(@_); # Matrices
   round bless
    {11=>$a->{11}*$b->{11}+$a->{12}*$b->{21}, 12=>$a->{11}*$b->{12}+$a->{12}*$b->{22},
     21=>$a->{21}*$b->{11}+$a->{22}*$b->{21}, 22=>$a->{21}*$b->{12}+$a->{22}*$b->{22},
    }; 
  }
 


=head3 matrixScalarDivide


Matrix=Matrix / non zero scalar

 
 sub matrixScalarDivide($$)
  {my ($a) = check(@_[0..0]); # Matrices
   my ($b) = @_[1..1];        # Scalar
   confess "$b is not a scalar" if ref($b);   
   confess "$b is zero"         if $b == 0;   
   round bless
    {11=>$a->{11}/$b, 12=>$a->{12}/$b,
     21=>$a->{21}/$b, 22=>$a->{22}/$b,
    }; 
  }
 


=head3 det


Determinant of matrix.

 
 sub det($)
  {my ($a) = check(@_); # Matrices
 
 +$a->{11}*$a->{22}
 -$a->{12}*$a->{21}
  }
 


=head3 inverse


Inverse of matrix

 
 sub inverse($)
  {my ($a) = check(@_); # Matrices
 
   my $d = det($a);
   return undef if $d == 0;
 
   round bless
    {11=> $a->{22}/$d, 21=>-$a->{21}/$d,
     12=>-$a->{12}/$d, 22=> $a->{11}/$d,
    }; 
  }
 


=head3 rotate


Rotation matrix: rotate anti-clockwise by t radians

 
 sub rotate($)
  {my ($a) = @_;
    bless
    {11=>cos($t), 21=>-sin($t),                              
     12=>sin($t), 22=> cos($t),                              
    }; 
  }
 


=head3 equals


Equals to within accuracy

 
 sub equals($$)
  {my ($a, $b) = check(@_); # Matrices
   abs($a->{11}-$b->{11}) < $accuracy and
   abs($a->{12}-$b->{12}) < $accuracy and
 
   abs($a->{21}-$b->{21}) < $accuracy and
   abs($a->{22}-$b->{22}) < $accuracy;
  }
 


=head2 Operators 


Operator overloads

 
 use overload
  '+'        => \&add3,      # Add two vectors
  '-'        => \&subtract3, # Subtract one vector from another
  '*'        => \&multiply3, # Times by a scalar, or vector dot product 
  '/'        => \&divide3,   # Divide by a scalar
  '!'        => \&det3,      # Determinant                       
  '=='       => \&equals3,   # Equals (to accuracy)
  '""'       => \&print3,    # Print
  'fallback' => FALSE;
 


=head3 add operator


Add operator.

 
 sub add3
  {my ($a, $b) = @_;
   $a->add($b);
  }
 


=head3 subtract operator


Subtract operator.

 
 sub subtract3
  {my ($a, $b, $c) = @_;
 
   return $a->subtract($b) if $b;
   negate($a);
  }
 


=head3 multiply operator


Multiply operator.

 
 sub multiply3
  {my ($a, $b) = @_;
   return $a->matrixScalarMultiply($b) unless ref($b);
   return $a->matrixVectorMultiply($b) if vector2Is($b);
   return $a->matrixMatrixMultiply($b) if is($b);
   confess "Cannot multiply $a by $b\n";
  }
 


=head3 divide operator


Divide operator.

 
 sub divide3
  {my ($a, $b, $c) = @_;
   if (!ref($b))
    {return $a->matrixScalarDivide($b)            unless $c;
     return $a->inverse->matrixScalarMultiply($b) if     $c;
    }
   else 
    {return $a->inverse->matrixVectorMultiply($b) if vector2Is($b);
     return $a->matrixMatrixMultiply($b->inverse) if is($b);
     confess "Cannot multiply $a by $b\n";
    }
  }
 


=head3 equals operator


Equals operator.

 
 sub equals3
  {my ($a, $b, $c) = @_;
   return $a->equals($b);
  }
 


=head3 determinant operator


Determinant of a matrix

 
 sub det3
  {my ($a, $b, $c) = @_;
   $a->det;
  }
 


=head3 print operator


Print a vector.

 
 sub print3
  {my ($a) = @_;
   return $a->print;
  }
 


=head2 Exports


Export L</matrix2>, L</identity>

 
 use exports qw(
   matrix2  ($$$$)
   new2v    ($$)
   identity ()
  );
 
 #_ Matrix2 ____________________________________________________________
 # Package loaded successfully
 #______________________________________________________________________
 
 1;
