       1 package Matrix;
       2 #
       3 # File: Matrix.pm
       4 # Author: Manish Sud <msud@san.rr.com>
       5 #
       6 # Copyright (C) 2018 Manish Sud. All rights reserved.
       7 #
       8 # This file is part of MayaChemTools.
       9 #
      10 # MayaChemTools is free software; you can redistribute it and/or modify it under
      11 # the terms of the GNU Lesser General Public License as published by the Free
      12 # Software Foundation; either version 3 of the License, or (at your option) any
      13 # later version.
      14 #
      15 # MayaChemTools is distributed in the hope that it will be useful, but without
      16 # any warranty; without even the implied warranty of merchantability of fitness
      17 # for a particular purpose.  See the GNU Lesser General Public License for more
      18 # details.
      19 #
      20 # You should have received a copy of the GNU Lesser General Public License
      21 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
      22 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
      23 # Boston, MA, 02111-1307, USA.
      24 #
      25
      26 use strict;
      27 use Carp;
      28 use Exporter;
      29 use Scalar::Util ();
      30 use Vector;
      31
      32 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      33
      34 @ISA = qw(Exporter);
      35 @EXPORT = qw(IsMatrix IdentityMatrix NewFromRows NewFromColumns NewFromDiagonal UnitMatrix ZeroMatrix);
      36 @EXPORT_OK = qw(SetValuePrintFormat);
      37
      38 %EXPORT_TAGS = (
      39                 all  => [@EXPORT, @EXPORT_OK]
      40                );
      41
      42 # Setup class variables...
      43 my($ClassName, $ValueFormat, $MatrixPrintStyle);
      44 _InitializeClass();
      45
      46 #
      47 # Using the following explicity overloaded operators, Perl automatically generates methods
      48 # for operations with no explicitly defined methods. Autogenerated methods are possible for
      49 # these operators:
      50 #
      51 #    o Arithmetic operators: += -= *= /= **= %= ++ -- x= .=
      52 #    o Increment and decrement: ++ --
      53 #
      54 # 'fallback' is set to 'false' to raise exception for all other operators.
      55 #
      56 use overload '""' => 'StringifyMatrix',
      57
      58   '@{}' => '_MatrixToArrayOperator',
      59
      60   '+' => '_MatrixAdditionOperator',
      61   '-' => '_MatrixSubtractionOperator',
      62   '*' => '_MatrixMultiplicationOperator',
      63   '/' => '_MatrixDivisionOperator',
      64   '**' => '_MatrixExponentiationOperator',
      65   '%' => '_MatrixModulusOperator',
      66
      67   'bool' => '_MatrixBooleanOperator',
      68   '!' => '_MatrixNotBooleanOperator',
      69
      70   '==' => '_MatrixEqualOperator',
      71   '!=' => '_MatrixNotEqualOperator',
      72   '<' => '_MatrixLessThanOperator',
      73   '<=' => '_MatrixLessThanEqualOperator',
      74   '>' => '_MatrixGreatarThanOperator',
      75   '>=' => '_MatrixGreatarThanEqualOperator',
      76
      77   'neg' => '_MatrixNegativeValueOperator',
      78
      79   'abs' => '_MatrixAbsoluteValueOperator',
      80   'exp' => '_MatrixExpNaturalBaseOperator',
      81   'log' => '_MatrixLogNaturalBaseOperator',
      82   'sqrt' => '_MatrixSquareRootOperator',
      83   'cos' => '_MatrixCosineOperator',
      84   'sin' => '_MatrixSineOperator',
      85
      86   'fallback' => undef;
      87
      88 # Class constructor...
      89 sub new {
      90   my($Class, $NumOfRows, $NumOfCols) = @_;
      91
      92   # Initialize object...
      93   my $This = {};
      94   bless $This, ref($Class) || $Class;
      95   $This->_InitializeMatrix($NumOfRows, $NumOfCols);
      96
      97   return $This;
      98 }
      99
     100 # Initialize object data...
     101 #
     102 sub _InitializeMatrix {
     103   my($This, $NumOfRows, $NumOfCols) = @_;
     104
     105   if (!(defined($NumOfRows) && defined($NumOfCols))) {
     106     croak "Error: ${ClassName}->_InitializeMatrix: NumOfRows and NumOfCols must be specified...";
     107   }
     108   if (!(($NumOfRows > 0) && ($NumOfRows > 0))) {
     109     croak "Error: ${ClassName}->_InitializeMatrix: NumOfRows and NumOfCols must be a positive number...";
     110   }
     111   # Initialize matrix elements to zero...
     112   @{$This->{Values}} = ();
     113
     114   my($RowIndex, @EmptyRow);
     115
     116   @EmptyRow = ();
     117   @EmptyRow = ('0') x $NumOfCols;
     118
     119   for $RowIndex (0 .. ($NumOfRows - 1)) {
     120     @{$This->{Values}[$RowIndex]} = ();
     121     @{$This->{Values}[$RowIndex]} = @EmptyRow;
     122   }
     123 }
     124
     125 # Initialize class ...
     126 sub _InitializeClass {
     127   #Class name...
     128   $ClassName = __PACKAGE__;
     129
     130   # Print style for matrix rows during StringifyMatrix operation.
     131   # Possible values: AllRowsInOneLine, OneRowPerLine
     132   #
     133   $MatrixPrintStyle = "AllRowsInOneLine";
     134
     135   # Print format for matrix values...
     136   $ValueFormat = "%g";
     137 }
     138
     139 # Get matrix size...
     140 #
     141 sub GetSize {
     142   my($This) = @_;
     143
     144   return ($This->GetNumOfRows(), $This->GetNumOfColumns());
     145 }
     146
     147 # Get matrix dimensions...
     148 #
     149 sub GetDimension {
     150   my($This) = @_;
     151
     152   return $This->GetSize();
     153 }
     154
     155 # Get number of rows in matrix
     156 #
     157 sub GetNumOfRows {
     158   my($This) = @_;
     159   my($NumOfRows);
     160
     161   # Size of row array...
     162   $NumOfRows = $#{$This->{Values}} + 1;
     163
     164   return $NumOfRows;
     165 }
     166
     167 # Get number of columns in matrix
     168 #
     169 sub GetNumOfColumns {
     170   my($This) = @_;
     171   my($NumOfCols);
     172
     173   # Size of column array for first row assuming sizes of columns are same...
     174   $NumOfCols = $#{$This->{Values}[0]} + 1;
     175
     176   return $NumOfCols;
     177 }
     178
     179 # Get reference to array holding matrix values in order to directly manipulate these values...
     180 #
     181 sub GetMatrixValuesReference {
     182   my($This) = @_;
     183
     184   return \@{$This->{Values}};
     185 }
     186
     187 # Copy matrix...
     188 #
     189 sub Copy {
     190   my($This) = @_;
     191   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Matrix);
     192
     193   # Create a new matrix...
     194   ($NumOfRows, $NumOfCols) = $This->GetSize();
     195   $Matrix = new Matrix($NumOfRows, $NumOfCols);
     196
     197   # Set matrix values...
     198   for $RowIndex (0 .. ($NumOfRows -1)) {
     199     for $ColIndex (0 .. ($NumOfCols -1)) {
     200       $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex];
     201     }
     202   }
     203   return $Matrix;
     204 }
     205
     206 # Create a new matrix using rows specified in one of the following formats:
     207 #   o List of vector objects
     208 #   o References to list of values
     209 #   o List of strings containing row values delimited by space
     210 #
     211 # Each row must contain the same number of values.
     212 #
     213 # This functionality can be either invoked as a class function or an
     214 # object method.
     215 #
     216 sub NewFromRows {
     217   my($FirstParameter, @OtherParamaters) = @_;
     218
     219   if (IsMatrix($FirstParameter)) {
     220     return _NewFromRowsOrColumns('FromRows', @OtherParamaters);
     221   }
     222   else {
     223     return _NewFromRowsOrColumns('FromRows', @_);
     224   }
     225 }
     226
     227 # Create a new matrix using columns specified in one of the following formats:
     228 #   o List of vector objects
     229 #   o References to list of values
     230 #   o List of strings containing columns values delimited by space
     231 #
     232 # Each columns must contain the same number of values.
     233 #
     234 # This functionality can be either invoked as a class function or an
     235 # object method.
     236 #
     237 sub NewFromColumns {
     238   my($FirstParameter, @OtherParamaters) = @_;
     239
     240   if (IsMatrix($FirstParameter)) {
     241     return _NewFromRowsOrColumns('FromColumns', @OtherParamaters);
     242   }
     243   else {
     244     return _NewFromRowsOrColumns('FromColumns', @_);
     245   }
     246 }
     247
     248 # Create a new matrix using diagonal values specified in one of the following formats:
     249 #   o A vector object
     250 #   o Reference to list of values
     251 #   o Strings containing diagonal values delimited by space
     252 #
     253 # This functionality can be either invoked as a class function or an
     254 # object method.
     255 #
     256 sub NewFromDiagonal {
     257   my($FirstParameter, @OtherParamaters) = @_;
     258
     259   if (IsMatrix($FirstParameter)) {
     260     return _NewFromDiagonal(@OtherParamaters);
     261   }
     262   else {
     263     return _NewFromDiagonal(@_);
     264   }
     265 }
     266
     267 # Create a new matrix using diagonal values specified in one of the following formats:
     268 #   o A vector object
     269 #   o Reference to list of values
     270 #   o Strings containing diagonal values delimited by space
     271 #
     272 sub _NewFromDiagonal {
     273   my(@SpecifiedDiagonalValues) = @_;
     274   my($ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs, $DiagonalValuesRef);
     275
     276   $ErrorMsgPrefix = "Error: ${ClassName}::_NewFromDiagonal";
     277   if (!@SpecifiedDiagonalValues) {
     278     croak "$ErrorMsgPrefix: No diagonal values specified...";
     279   }
     280
     281   # Collect specified diagonal values...
     282   $CheckSizes = 0; $CombineValues = 1;
     283   $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedDiagonalValues);
     284   $DiagonalValuesRef = $ValuesRefs->[0];
     285
     286   # Create a new matrix...
     287   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex);
     288
     289   $NumOfRows = @{$DiagonalValuesRef};
     290   $NumOfCols = $NumOfRows;
     291
     292   $Matrix = new Matrix($NumOfRows, $NumOfCols);
     293
     294   # Set diagonal values...
     295   for $RowIndex (0 .. ($NumOfRows - 1)) {
     296     $Matrix->{Values}[$RowIndex][$RowIndex] = $DiagonalValuesRef->[$RowIndex];
     297   }
     298
     299   return $Matrix;
     300 }
     301
     302 # Create a new matrix using rows or columns specified in one of the following formats:
     303 #   o List of vector objects
     304 #   o References to list of values
     305 #   o List of strings containing row values delimited by space
     306 #
     307 # Each row or column must contain the same number of values.
     308 #
     309 sub _NewFromRowsOrColumns {
     310   my($Mode, @SpecifiedValues) = @_;
     311
     312   if ($Mode !~ /^(FromRows|FromColumns)$/i) {
     313     croak "Error: ${ClassName}::_NewFromRowsOrColumns: Unknown mode: $Mode...";
     314   }
     315   my($ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs);
     316
     317   # Retrieve information about specified values and make sure similar number of values
     318   # are specified for each row or column...
     319   if ($Mode =~ /^FromRows$/i) {
     320     $ErrorMsgPrefix = "Error: ${ClassName}::_NewFromRows";
     321   }
     322   else {
     323     $ErrorMsgPrefix = "Error: ${ClassName}::_NewFromColumns";
     324   }
     325   $CheckSizes = 1; $CombineValues = 0;
     326   $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
     327
     328   # Create a new matrix...
     329   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $RowMode, $Value);
     330
     331   if ($Mode =~ /^FromRows$/i) {
     332     $NumOfRows = scalar @{$ValuesRefs};
     333     $NumOfCols = scalar @{$ValuesRefs->[0]};
     334     $RowMode = 1;
     335   }
     336   elsif ($Mode =~ /^FromColumns$/i) {
     337     $NumOfRows = scalar @{$ValuesRefs->[0]};
     338     $NumOfCols = scalar @{$ValuesRefs};
     339     $RowMode = 0;
     340   }
     341   $Matrix = new Matrix($NumOfRows, $NumOfCols);
     342
     343   # Setup matrix values...
     344   for $RowIndex (0 .. ($NumOfRows - 1)) {
     345     for $ColIndex (0 .. ($NumOfCols - 1)) {
     346       $Value = $RowMode ? $ValuesRefs->[$RowIndex]->[$ColIndex]: $ValuesRefs->[$ColIndex]->[$RowIndex];
     347       $Matrix->{Values}[$RowIndex][$ColIndex] = $Value;
     348     }
     349   }
     350
     351   return $Matrix;
     352 }
     353
     354 # Process specified matrix values in any of the following supported formats:
     355 #
     356 #   o List of vector objects
     357 #   o References to list of values
     358 #   o List of strings containing row values delimited by space
     359 #
     360 # And return a reference to an array containing references to arrays with specified values.
     361 #
     362 # Value of CombineValuesStatus determines whether all the values specified are combined
     363 # into one array and return its reference as the only entry in the array being returned.
     364 #
     365 sub _ProcessSpecifiedMatrixValues {
     366   my($ErrorMsgPrefix, $CheckSizesStatus, $CombineValuesStatus, @SpecifiedValues) = @_;
     367   my($Value, $TypeOfValue, @ValuesRefs);
     368
     369   @ValuesRefs = ();
     370   if (!@SpecifiedValues) {
     371     croak "$ErrorMsgPrefix: No values specified...";
     372   }
     373
     374   # Collect values...
     375   for $Value (@SpecifiedValues) {
     376     $TypeOfValue = ref $Value;
     377
     378     if (Vector::IsVector($Value)) {
     379       # Feference to vector object...
     380       my($ValuesRef);
     381       $ValuesRef = $Value->GetValues();
     382       if (!@{$ValuesRef}) {
     383         croak "$ErrorMsgPrefix: Specified vector object must contain some values...";
     384       }
     385       push @ValuesRefs, $ValuesRef;
     386     }
     387     elsif ($TypeOfValue =~ /^ARRAY/) {
     388       # Refernece to an array...
     389       if (!@{$Value}) {
     390         croak "$ErrorMsgPrefix: Specified array reference must contain some values...";
     391       }
     392       push @ValuesRefs, $Value;
     393     }
     394     elsif ($TypeOfValue eq '') {
     395       # String value...
     396       my(@Values);
     397       @Values = split(' ', $Value);
     398       if (!@Values) {
     399         croak "$ErrorMsgPrefix: Specified string must contain some values...";
     400       }
     401       push @ValuesRefs, \@Values;
     402     }
     403     else {
     404       croak "$ErrorMsgPrefix: Value format, $TypeOfValue, of a specified value to be added to matrix object is not supported...";
     405     }
     406   }
     407
     408   # Combine all specified values into one array...
     409   if ($CombineValuesStatus) {
     410     my($ValuesRef, @Values);
     411
     412     @Values = ();
     413     for $ValuesRef (@ValuesRefs) {
     414       push @Values, @{$ValuesRef};
     415     }
     416     @ValuesRefs = ();
     417     push @ValuesRefs, \@Values;
     418   }
     419
     420   # Make sure reference to all specified value arrays contain the same number of values...
     421   if ($CheckSizesStatus) {
     422     my($Index, $FirstValueSize);
     423     $FirstValueSize = $#{$ValuesRefs[0]};
     424     for $Index (1 .. $#ValuesRefs) {
     425       if ($FirstValueSize != $#{$ValuesRefs[$Index]}) {
     426         croak "$ErrorMsgPrefix: Number of values in each specified value type to be added to matrix object must be same...";
     427       }
     428     }
     429   }
     430
     431   return \@ValuesRefs;
     432 }
     433
     434 # Create a new zero matrix of specified size or default size of 3 x 3.
     435 #
     436 # This functionality can be either invoked as a class function or an
     437 # object method.
     438 #
     439 sub ZeroMatrix (;$$$) {
     440   my($FirstParameter, $SecondParameter, $ThirdParameter) = @_;
     441   my($This, $NumOfRows, $NumOfCols, $Matrix);
     442
     443   $This = undef;
     444   if (defined($FirstParameter) && IsMatrix($FirstParameter)) {
     445     ($This, $NumOfRows, $NumOfCols) = ($FirstParameter, $SecondParameter, $ThirdParameter);
     446   }
     447   else {
     448     ($This, $NumOfRows, $NumOfCols) = (undef, $FirstParameter, $SecondParameter);
     449   }
     450   ($NumOfRows, $NumOfCols) = (defined($NumOfRows) && defined($NumOfCols)) ? ($NumOfRows, $NumOfCols) : (3, 3);
     451
     452   # Set up a new zero matrix
     453   $Matrix = new Matrix($NumOfRows, $NumOfCols);
     454
     455   return $Matrix;
     456 }
     457
     458 # Create a new unit matrix of specified size or default size of 3 x 3.
     459 #
     460 # This functionality can be either invoked as a class function or an
     461 # object method.
     462 #
     463 sub UnitMatrix (;$$$) {
     464   my($FirstParameter, $SecondParameter, $ThirdParameter) = @_;
     465   my($This, $NumOfRows, $NumOfCols, $Matrix, $RowIndex);
     466
     467   $This = undef;
     468   if (defined($FirstParameter) && IsMatrix($FirstParameter)) {
     469     ($This, $NumOfRows, $NumOfCols) = ($FirstParameter, $SecondParameter, $ThirdParameter);
     470   }
     471   else {
     472     ($This, $NumOfRows, $NumOfCols) = (undef, $FirstParameter, $SecondParameter);
     473   }
     474   ($NumOfRows, $NumOfCols) = (defined($NumOfRows) && defined($NumOfCols)) ? ($NumOfRows, $NumOfCols) : (3, 3);
     475
     476   # Set up a new zero matrix
     477   $Matrix = new Matrix($NumOfRows, $NumOfCols);
     478
     479   if ($NumOfRows != $NumOfCols) {
     480     carp "Warning: ${ClassName}::UnitMatrix: Specified matrix, $NumOfRows x $NumOfCols, is not a square matrix...";
     481   }
     482
     483   # Initialize diagonal elements to 1...
     484   for $RowIndex (0 .. ($NumOfRows - 1)) {
     485     $Matrix->{Values}[$RowIndex][$RowIndex] = 1.0;
     486   }
     487
     488   return $Matrix;
     489 }
     490
     491 # Identity matrix of specified size or size 3 x 3...
     492 #
     493 sub IdentityMatrix (;$$$) {
     494   my($FirstParameter, $SecondParameter, $ThirdParameter) = @_;
     495
     496   return UnitMatrix($FirstParameter, $SecondParameter, $ThirdParameter);
     497 }
     498
     499 # Set all matrix values to 0s...
     500 #
     501 sub Zero {
     502   my($This) = @_;
     503
     504   return $This->SetAllValues(0.0);
     505 }
     506
     507 # Set all matrix values to 1s...
     508 #
     509 sub One {
     510   my($This) = @_;
     511
     512   return $This->SetAllValues(1.0);
     513 }
     514
     515 # Get a matrix value with row and column indicies starting from 0...
     516 #
     517 sub GetValue {
     518   my($This, $RowIndex, $ColIndex, $SkipIndexCheck) = @_;
     519
     520   if ($SkipIndexCheck) {
     521     $This->_GetValue($RowIndex, $ColIndex);
     522   }
     523
     524   $This->_ValidateRowAndColumnIndicies("Error: ${ClassName}::GetValue", $RowIndex, $ColIndex);
     525
     526   return $This->_GetValue($RowIndex, $ColIndex);
     527 }
     528
     529 # Get a matrix value...
     530 #
     531 sub _GetValue {
     532   my($This, $RowIndex, $ColIndex) = @_;
     533
     534   return $This->{Values}[$RowIndex][$ColIndex];
     535 }
     536
     537 # Set a matrix value with row and column indicies starting from 0...
     538 #
     539 sub SetValue {
     540   my($This, $RowIndex, $ColIndex, $Value, $SkipIndexCheck) = @_;
     541
     542   if ($SkipIndexCheck) {
     543     $This->_SetValue($RowIndex, $ColIndex, $Value);
     544   }
     545
     546   $This->_ValidateRowAndColumnIndicies("Error: ${ClassName}::SetValue", $RowIndex, $ColIndex);
     547
     548   return $This->_SetValue($RowIndex, $ColIndex, $Value);
     549 }
     550
     551 # Set a matrix value...
     552 #
     553 sub _SetValue {
     554   my($This, $RowIndex, $ColIndex, $Value) = @_;
     555
     556   $This->{Values}[$RowIndex][$ColIndex] = $Value;
     557
     558   return $This;
     559 }
     560
     561 # Set all matrix values to a specified value...
     562 #
     563 sub SetAllValues {
     564   my($This, $Value) = @_;
     565   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
     566
     567   ($NumOfRows, $NumOfCols) = $This->GetSize();
     568   for $RowIndex (0 .. ($NumOfRows - 1)) {
     569     for $ColIndex (0 .. ($NumOfCols - 1)) {
     570       $This->{Values}[$RowIndex][$ColIndex] = $Value;
     571     }
     572   }
     573   return $This;
     574 }
     575
     576 # Set values of a row in a matrix value with row index starting from 0...
     577 #
     578 sub SetRowValues {
     579   my($This, $RowIndex, @SpecifiedValues) = @_;
     580   my($NumOfRows, $NumOfCols, $ColIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs, $RowValuesRef, $NumOfRowValues);
     581
     582   $ErrorMsgPrefix = "Error: ${ClassName}->SetRowValues";
     583
     584   ($NumOfRows, $NumOfCols) = $This->GetSize();
     585   $This->_ValidateRowIndex($ErrorMsgPrefix, $RowIndex);
     586
     587   # Collect specified row values...
     588   $CheckSizes = 0; $CombineValues = 1;
     589   $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
     590   $RowValuesRef = $ValuesRefs->[0];
     591
     592   # Check number of specified row values...
     593   $NumOfRowValues = @{$RowValuesRef};
     594   if ($NumOfRowValues != $NumOfCols) {
     595     croak "$ErrorMsgPrefix: Number of specified row values, $NumOfRowValues, must be equal to number of row values, $NumOfCols, in matrix...";
     596   }
     597
     598   # Set row values...
     599   for $ColIndex (0 .. ($NumOfRowValues - 1)) {
     600     $This->{Values}[$RowIndex][$ColIndex] = $RowValuesRef->[$ColIndex];
     601   }
     602   return $This;
     603 }
     604
     605 # Add new row values to a matrix...
     606 #
     607 sub AddRowValues {
     608   my($This, @SpecifiedValues) = @_;
     609   my($NumOfRows, $NumOfCols, $RowIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $RowValueRef, $RowValuesRefs, $NumOfNewRows, $NumOfNewCols);
     610
     611   $ErrorMsgPrefix = "Error: ${ClassName}->AddRowValues";
     612
     613   ($NumOfRows, $NumOfCols) = $This->GetSize();
     614
     615   # Collect specified row values...
     616   $CheckSizes = 1; $CombineValues = 0;
     617   $RowValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
     618
     619   # Check number of specified row values...
     620   $NumOfNewRows = scalar @{$RowValuesRefs};
     621   $NumOfNewCols = scalar @{$RowValuesRefs->[0]};
     622
     623   if ($NumOfNewCols != $NumOfCols) {
     624     croak "$ErrorMsgPrefix: Number of values in each specified row, $NumOfNewCols, must be equal to number of row values, $NumOfCols, in matrix...";
     625   }
     626
     627   # Add each row to the matrix...
     628   $RowIndex = $NumOfRows - 1;
     629   for $RowValueRef (@{$RowValuesRefs}) {
     630     $RowIndex++;
     631     @{$This->{Values}[$RowIndex]} = @{$RowValueRef};
     632   }
     633
     634   return $This;
     635 }
     636
     637 # Get values of a row in matrix as an array. In scalar context, number of row
     638 # values is returned...
     639 #
     640 sub GetRowValues {
     641   my($This, $RowIndex) = @_;
     642
     643   return $This->_GetRowOrColumnValues('AsArray', 'FromRow', $RowIndex);
     644 }
     645
     646 # Get values of a row in matrix as a vector object...
     647 #
     648 sub GetRowValuesAsVector {
     649   my($This, $RowIndex) = @_;
     650
     651   return $This->_GetRowOrColumnValues('AsVector', 'FromRow', $RowIndex);
     652 }
     653
     654 # Get values of a row as row matrix object...
     655 #
     656 sub GetRowValuesAsRowMatrix {
     657   my($This, $RowIndex) = @_;
     658
     659   return $This->_GetRowOrColumnValues('AsRowMatrix', 'FromRow', $RowIndex);
     660 }
     661
     662 # Get values of a row as column matrix object...
     663 #
     664 sub GetRowValuesAsColumnMatrix {
     665   my($This, $RowIndex) = @_;
     666
     667   return $This->_GetRowOrColumnValues('AsColumnMatrix', 'FromRow', $RowIndex);
     668 }
     669
     670 # Get values of a row in matrix as a space delimited string...
     671 #
     672 sub GetRowValuesAsString {
     673   my($This, $RowIndex) = @_;
     674
     675   return $This->_GetRowOrColumnValues('AsString', 'FromRow', $RowIndex);
     676 }
     677
     678 # Set values of a column in a matrix value with row index starting from 0...
     679 #
     680 sub SetColumnValues {
     681   my($This, $ColIndex, @SpecifiedValues) = @_;
     682   my($NumOfRows, $NumOfCols, $RowIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $ValuesRefs, $ColValuesRef, $NumOfColValues);
     683
     684   $ErrorMsgPrefix = "Error: ${ClassName}->SetColumnValues";
     685
     686   ($NumOfRows, $NumOfCols) = $This->GetSize();
     687   $This->_ValidateColumnIndex($ErrorMsgPrefix, $ColIndex);
     688
     689   # Collect specified row values...
     690   $CheckSizes = 0; $CombineValues = 1;
     691   $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
     692   $ColValuesRef = $ValuesRefs->[0];
     693
     694   # Check number of specified col values...
     695   $NumOfColValues = @{$ColValuesRef};
     696   if ($NumOfColValues != $NumOfRows) {
     697     croak "$ErrorMsgPrefix: Number of specified col values, $NumOfColValues, must be equal to number of column values, $NumOfRows, in matrix...";
     698   }
     699
     700   # Set col values...
     701   for $RowIndex (0 .. ($NumOfColValues - 1)) {
     702     $This->{Values}[$RowIndex][$ColIndex] = $ColValuesRef->[$RowIndex];
     703   }
     704   return $This;
     705 }
     706
     707 # Add new column values to a matrix...
     708 #
     709 sub AddColumnValues {
     710   my($This, @SpecifiedValues) = @_;
     711   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $ErrorMsgPrefix, $CheckSizes, $CombineValues, $ColValueRef, $ColValuesRefs, $NumOfNewRows, $NumOfNewCols);
     712
     713   $ErrorMsgPrefix = "Error: ${ClassName}->AddColumnValues";
     714
     715   ($NumOfRows, $NumOfCols) = $This->GetSize();
     716
     717   # Collect specified column values...
     718   $CheckSizes = 1; $CombineValues = 0;
     719   $ColValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedValues);
     720
     721   # Check number of specified column values...
     722   $NumOfNewCols = scalar @{$ColValuesRefs};
     723   $NumOfNewRows = scalar @{$ColValuesRefs->[0]};
     724
     725   if ($NumOfNewRows != $NumOfRows) {
     726     croak "$ErrorMsgPrefix: Number of values in each specified column, $NumOfNewRows, must be equal to number of column values, $NumOfRows, in matrix...";
     727   }
     728
     729   # Add each column to the matrix...
     730   $ColIndex = $NumOfCols - 1;
     731   for $ColValueRef (@{$ColValuesRefs}) {
     732     $ColIndex++;
     733     for $RowIndex (0 .. ($NumOfCols - 1)) {
     734       $This->{Values}[$RowIndex][$ColIndex] = $ColValueRef->[$RowIndex];
     735     }
     736   }
     737
     738   return $This;
     739 }
     740
     741 # Get values of a column in matrix as an array. In scalar context, number of column
     742 # values is returned...
     743 #
     744 sub GetColumnValues {
     745   my($This, $ColIndex) = @_;
     746
     747   return $This->_GetRowOrColumnValues('AsArray', 'FromColumn', $ColIndex);
     748 }
     749
     750 # Get values of a column in matrix as a vector object...
     751 #
     752 sub GetColumnValuesAsVector {
     753   my($This, $ColIndex) = @_;
     754
     755   return $This->_GetRowOrColumnValues('AsVector', 'FromColumn', $ColIndex);
     756 }
     757
     758 # Get values of a column as row matrix object...
     759 #
     760 sub GetColumnValuesAsRowMatrix {
     761   my($This, $ColIndex) = @_;
     762
     763   return $This->_GetRowOrColumnValues('AsRowMatrix', 'FromColumn', $ColIndex);
     764 }
     765
     766 # Get values of a column as column matrix object...
     767 #
     768 sub GetColumnValuesAsColumnMatrix {
     769   my($This, $ColIndex) = @_;
     770
     771   return $This->_GetRowOrColumnValues('AsColumnMatrix', 'FromColumn', $ColIndex);
     772 }
     773
     774 # Get values of a column in matrix as a space delimited string...
     775 #
     776 sub GetColumnValuesAsString {
     777   my($This, $ColIndex) = @_;
     778
     779   return $This->_GetRowOrColumnValues('AsString', 'FromColumn', $ColIndex);
     780 }
     781
     782 # Get row or column values...
     783 #
     784 sub _GetRowOrColumnValues {
     785   my($This, $Mode, $ValueMode, $ValueModeIndex) = @_;
     786
     787   if ($Mode !~ /^(AsArray|AsVector|AsRowMatrix|AsColumnMatrix|AsString)$/i) {
     788     croak "Error: ${ClassName}->_GetRowOrColumnValues: Unknown mode, $Mode, specified...";
     789   }
     790   if ($ValueMode !~ /^(FromRow|FromColumn)$/i) {
     791     croak "Error: ${ClassName}->_GetRowOrColumnValues: Unknown value mode, $ValueMode, specified...";
     792   }
     793
     794   # Setup error message prefix...
     795   my($ErrorMsgPrefix);
     796
     797   $ErrorMsgPrefix = "${ClassName}->_GetRowOrColumnValues";
     798   if ($ValueMode =~ /^FromRow$/i) {
     799     $ErrorMsgPrefix = "Error: ${ClassName}->GetRowValues${Mode}";
     800   }
     801   elsif ($ValueMode =~ /^FromColumn$/i) {
     802     $ErrorMsgPrefix = "Error: ${ClassName}->GetColumnValues${Mode}";
     803   }
     804
     805   # Validate specified index and collect values...
     806   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, @Values);
     807
     808   @Values  = ();
     809   ($NumOfRows, $NumOfCols) = $This->GetSize();
     810
     811   if ($ValueMode =~ /^FromRow$/i) {
     812     $RowIndex = $ValueModeIndex;
     813     $This->_ValidateRowIndex($ErrorMsgPrefix, $RowIndex);
     814
     815     for $ColIndex (0 .. ($NumOfCols - 1)) {
     816       push @Values, $This->{Values}[$RowIndex][$ColIndex];
     817     }
     818   }
     819   elsif ($ValueMode =~ /^FromColumn$/i) {
     820     $ColIndex = $ValueModeIndex;
     821     $This->_ValidateColumnIndex($ErrorMsgPrefix, $ColIndex);
     822
     823     for $RowIndex (0 .. ($NumOfRows - 1)) {
     824       push @Values, $This->{Values}[$RowIndex][$ColIndex];
     825     }
     826   }
     827
     828   # Return values...
     829   if ($Mode =~ /^AsRowMatrix$/i) {
     830     return NewFromRows(\@Values);
     831   }
     832   elsif ($Mode =~ /^AsColumnMatrix$/i) {
     833     return NewFromColumns(\@Values);
     834   }
     835   elsif ($Mode =~ /^AsVector$/i) {
     836     return new Vector(@Values);
     837   }
     838   elsif ($Mode =~ /^AsString$/i) {
     839     return join(' ', @Values);
     840   }
     841   else {
     842     return wantarray ? @Values : scalar @Values;
     843   }
     844 }
     845
     846 # Set values of the diagonal in a square matrix...
     847 #
     848 sub SetDiagonalValues {
     849   my($This, @SpecifiedDiagonalValues) = @_;
     850   my($ErrorMsgPrefix, $NumOfRows, $NumOfCols, $RowIndex, $CheckSizes, $CombineValues, $ValuesRefs, $NumOfDiagonalValues, $DiagonalValuesRef);
     851
     852   $ErrorMsgPrefix = "Error: ${ClassName}->SetDiagonalValues";
     853   if (!@SpecifiedDiagonalValues) {
     854     croak "$ErrorMsgPrefix: No diagonal values specified...";
     855   }
     856
     857   ($NumOfRows, $NumOfCols) = $This->GetSize();
     858   if ($NumOfRows != $NumOfCols) {
     859     croak "Error: $ErrorMsgPrefix: Specified matrix, $NumOfRows x $NumOfCols, is not a square matrix...";
     860   }
     861
     862   # Collect specified diagonal values...
     863   $CheckSizes = 0; $CombineValues = 1;
     864   $ValuesRefs = _ProcessSpecifiedMatrixValues($ErrorMsgPrefix, $CheckSizes, $CombineValues, @SpecifiedDiagonalValues);
     865   $DiagonalValuesRef = $ValuesRefs->[0];
     866   $NumOfDiagonalValues = @{$DiagonalValuesRef};
     867
     868   if ($NumOfDiagonalValues != $NumOfRows) {
     869     croak "Error: $ErrorMsgPrefix: Number of specified diagonal values, $NumOfDiagonalValues, must be equal to number of rows, $NumOfRows, in square matrix...";
     870   }
     871
     872   # Set diagonal values...
     873   for $RowIndex (0 .. ($NumOfRows - 1)) {
     874     $This->{Values}[$RowIndex][$RowIndex] = $DiagonalValuesRef->[$RowIndex];
     875   }
     876
     877   return $This;
     878 }
     879
     880 # Get values of the diagonal in a square matrix as an array. In scalar context, number of
     881 # diagonal values is returned...
     882 #
     883 sub GetDiagonalValues {
     884   my($This) = @_;
     885
     886   return $This->_GetDiagonalValues('AsArray');
     887 }
     888
     889 # Get values of the diagonal in a square matrix as vector object...
     890 #
     891 sub GetDiagonalValuesAsVector {
     892   my($This) = @_;
     893
     894   return $This->_GetDiagonalValues('AsVector');
     895 }
     896
     897 # Get values of the diagonal in a square matrix as row matrix object
     898 #
     899 sub GetDiagonalValuesAsRowMatrix {
     900   my($This) = @_;
     901
     902   return $This->_GetDiagonalValues('AsRowMatrix');
     903 }
     904
     905 # Get values of the diagonal in a square matrix as column matrix object
     906 #
     907 sub GetDiagonalValuesAsColumnMatrix {
     908   my($This) = @_;
     909
     910   return $This->_GetDiagonalValues('AsColumnMatrix');
     911 }
     912
     913 # Get values of the diagonal in a square matrix as a space delimited string...
     914 #
     915 sub GetDiagonalValuesAsString {
     916   my($This) = @_;
     917
     918   return $This->_GetDiagonalValues('AsString');
     919 }
     920
     921 # Get diagonal values...
     922 sub _GetDiagonalValues {
     923   my($This, $Mode) = @_;
     924
     925   if ($Mode !~ /^(AsArray|AsVector|AsRowMatrix|AsColumnMatrix|AsString)$/i) {
     926     croak "Error: ${ClassName}->_GetDiagonalValues: Unknown mode, $Mode, specified...";
     927   }
     928
     929   # Make sure it's a square matrix...
     930   my($NumOfRows, $NumOfCols, $ErrorMsgPrefix);
     931
     932   $ErrorMsgPrefix = "${ClassName}->_GetDiagonalValues${Mode}";
     933   ($NumOfRows, $NumOfCols) = $This->GetSize();
     934   if ($NumOfRows != $NumOfCols) {
     935     croak "Error: $ErrorMsgPrefix: Specified matrix, $NumOfRows x $NumOfCols, is not a square matrix...";
     936   }
     937
     938   # Collect values...
     939   my($RowIndex, @Values);
     940   @Values = ();
     941
     942   for $RowIndex (0 .. ($NumOfRows - 1)) {
     943     push @Values, $This->{Values}[$RowIndex][$RowIndex];
     944   }
     945
     946   # Return values...
     947   if ($Mode =~ /^AsRowMatrix$/i) {
     948     return NewFromRows(\@Values);
     949   }
     950   elsif ($Mode =~ /^AsColumnMatrix$/i) {
     951     return NewFromColumns(\@Values);
     952   }
     953   elsif ($Mode =~ /^AsVector$/i) {
     954     return new Vector(@Values);
     955   }
     956   elsif ($Mode =~ /^AsString$/i) {
     957     return join(' ', @Values);
     958   }
     959   else {
     960     return wantarray ? @Values : scalar @Values;
     961   }
     962 }
     963
     964 # Is it a square matrix?
     965 #
     966 sub IsSquare {
     967   my($This) = @_;
     968   my($NumOfRows, $NumOfCols) = $This->GetSize();
     969
     970   return ($NumOfRows == $NumOfCols) ? 1 : 0;
     971 }
     972
     973 # Is it a unit matrix?
     974 #
     975 # A matrix is a unit matrix:
     976 #   o It's a square matrix
     977 #   o All its diagonal elements are ones and its off-diagonal elements are zeros
     978 #
     979 sub IsUnit {
     980   my($This) = @_;
     981
     982   # Is is a square matrix?
     983   if (!$This->IsSquare()) {
     984     return 0;
     985   }
     986
     987   # Check matrix values...
     988   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $ExpectedValue);
     989   ($NumOfRows, $NumOfCols) = $This->GetSize();
     990
     991   for $RowIndex (0 .. ($NumOfRows - 1)) {
     992     for $ColIndex (0 .. ($NumOfCols - 1)) {
     993       $ExpectedValue = ($RowIndex == $ColIndex) ? 1.0 : 0.0;
     994       if ($This->{Values}[$RowIndex][$ColIndex] != $ExpectedValue) {
     995         return 0;
     996       }
     997     }
     998   }
     999   return 1;
    1000 }
    1001
    1002 # Is it an identity matrix?
    1003 #
    1004 sub IsIdentity {
    1005   my($This) = @_;
    1006
    1007   return $This->IsUnit();
    1008 }
    1009
    1010 # Is it a diagonal matrix?
    1011 #
    1012 # A matrix is a diagonal matrix:
    1013 #   o It's a square matrix
    1014 #   o All its off-diagonal elements are zeros and its diagonal elements may or may not
    1015 #     be zeros
    1016 #
    1017 #
    1018 sub IsDiagonal {
    1019   my($This) = @_;
    1020
    1021   # Is is a square matrix?
    1022   if (!$This->IsSquare()) {
    1023     return 0;
    1024   }
    1025
    1026   # Check off-diagonal matrix values...
    1027   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1028   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1029
    1030   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1031     COLINDEX: for $ColIndex (0 .. ($NumOfCols - 1)) {
    1032       if ($RowIndex == $ColIndex) {
    1033         next COLINDEX;
    1034       }
    1035       if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
    1036         return 0;
    1037       }
    1038     }
    1039   }
    1040   return 1;
    1041 }
    1042
    1043 # Is it a lower bidiagonal matrix?
    1044 #
    1045 # A matrix is a lower bidiagonal matrix:
    1046 #   o It's a square matrix
    1047 #   o All its main diagonal and lower diagonal elements are non-zeros and all its
    1048 #     other elements are zeros
    1049 #
    1050 sub IsLowerBiDiagonal {
    1051   my($This) = @_;
    1052
    1053   # Is is a square matrix?
    1054   if (!$This->IsSquare()) {
    1055     return 0;
    1056   }
    1057
    1058   # Check matrix values...
    1059   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Value);
    1060   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1061
    1062   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1063     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1064       $Value = $This->{Values}[$RowIndex][$ColIndex];
    1065       if ($RowIndex == $ColIndex) {
    1066         # Main diagonal...
    1067         if ($Value == 0.0) {
    1068           return 0;
    1069         }
    1070       }
    1071       elsif ($RowIndex == ($ColIndex + 1)) {
    1072         # Lower diagonal...
    1073         if ($Value == 0.0) {
    1074           return 0;
    1075         }
    1076       }
    1077       else {
    1078         # Other elements...
    1079         if ($Value != 0.0) {
    1080           return 0;
    1081         }
    1082       }
    1083     }
    1084   }
    1085   return 1;
    1086 }
    1087
    1088 # Is it an upper bidiagonal matrix?
    1089 #
    1090 # A matrix is an upper bidiagonal matrix:
    1091 #   o It's a square matrix
    1092 #   o All its main diagonal and upper diagonal elements are non-zeros and all its
    1093 #     other elements are zeros
    1094 #
    1095 sub IsUpperBiDiagonal {
    1096   my($This) = @_;
    1097
    1098   # Is is a square matrix?
    1099   if (!$This->IsSquare()) {
    1100     return 0;
    1101   }
    1102   # Check matrix values...
    1103   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Value);
    1104   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1105
    1106   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1107     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1108       $Value = $This->{Values}[$RowIndex][$ColIndex];
    1109       if ($RowIndex == $ColIndex) {
    1110         # Main diagonal...
    1111         if ($Value == 0.0) {
    1112           return 0;
    1113         }
    1114       }
    1115       elsif ($RowIndex == ($ColIndex - 1)) {
    1116         # Upper diagonal...
    1117         if ($Value == 0.0) {
    1118           return 0;
    1119         }
    1120       }
    1121       else {
    1122         # Other elements...
    1123         if ($Value != 0.0) {
    1124           return 0;
    1125         }
    1126       }
    1127     }
    1128   }
    1129   return 1;
    1130 }
    1131
    1132 # Is it a bidiagonal matrix?
    1133 #
    1134 # A matrix is a bidiagonal matrix:
    1135 #
    1136 sub IsBiDiagonal {
    1137   my($This) = @_;
    1138
    1139   return ($This->IsUpperBiDiagonal() || $This->IsLowerBiDiagonal()) ? 1 : 0;
    1140 }
    1141
    1142 # Is it a tridiagonal matrix?
    1143 #
    1144 # A matrix is a  tribidiagonal matrix:
    1145 #   o It's a square matrix
    1146 #   o All its main diagonal, upper diagonal, and lower diagonal elements are non-zeros and all its
    1147 #     other elements are zeros
    1148 #
    1149 #
    1150 sub IsTriDiagonal {
    1151   my($This) = @_;
    1152
    1153   # Is is a square matrix?
    1154   if (!$This->IsSquare()) {
    1155     return 0;
    1156   }
    1157
    1158   # Check matrix values...
    1159   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $Value);
    1160   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1161
    1162   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1163     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1164       $Value = $This->{Values}[$RowIndex][$ColIndex];
    1165       if ($RowIndex == $ColIndex) {
    1166         # Main diagonal...
    1167         if ($Value == 0.0) {
    1168           return 0;
    1169         }
    1170       }
    1171       elsif ($RowIndex == ($ColIndex - 1)) {
    1172         # Upper diagonal...
    1173         if ($Value == 0.0) {
    1174           return 0;
    1175         }
    1176       }
    1177       elsif ($RowIndex == ($ColIndex + 1)) {
    1178         # Lower diagonal...
    1179         if ($Value == 0.0) {
    1180           return 0;
    1181         }
    1182       }
    1183       else {
    1184         # Other elements...
    1185         if ($Value != 0.0) {
    1186           return 0;
    1187         }
    1188       }
    1189     }
    1190   }
    1191   return 1;
    1192 }
    1193
    1194 # Is it a lower triangular matrix?
    1195 #
    1196 # A matrix is a lower triangular matrix:
    1197 #   o It's a square matrix
    1198 #   o All its entries above the main diagonal are zero
    1199 #
    1200 sub IsLowerTriangular {
    1201   my($This) = @_;
    1202
    1203   return $This->_IsLowerTriangularMatrix();
    1204 }
    1205
    1206 # Is it a left triangular matrix?
    1207 #
    1208 # A matrix is a left triangular matrix:
    1209 #   o It's a square matrix
    1210 #   o All its entries above the main diagonal are zero
    1211 #
    1212 sub IsLeftTriangular {
    1213   my($This) = @_;
    1214
    1215   return $This->IsLowerTriangular();
    1216 }
    1217
    1218 # Is it a strictly lower triangular matrix?
    1219 #
    1220 # A matrix is a strictly lower triangular matrix:
    1221 #   o It's a square matrix
    1222 #   o All its entries on and above the main diagonal are zero
    1223 #
    1224 sub IsStrictlyLowerTriangular {
    1225   my($This) = @_;
    1226   my($DiagonalValue);
    1227
    1228   $DiagonalValue = 0;
    1229
    1230   return $This->_IsLowerTriangularMatrix($DiagonalValue);
    1231 }
    1232
    1233 # Is it an unit lower triangular matrix?
    1234 #
    1235 # A matrix is an unit lower triangular matrix:
    1236 #   o It's a square matrix
    1237 #   o All its entries main diagonal are one
    1238 #   o All its entries above the main diagonal are zero
    1239 #
    1240 sub IsUnitLowerTriangular {
    1241   my($This) = @_;
    1242   my($DiagonalValue);
    1243
    1244   $DiagonalValue = 1;
    1245
    1246   return $This->_IsLowerTriangularMatrix($DiagonalValue);
    1247 }
    1248
    1249 # Is it a lower unitriangular matrix?
    1250 #
    1251 sub IsLowerUniTriangular {
    1252   my($This) = @_;
    1253
    1254   return $This->IsUnitLowerTriangular();
    1255 }
    1256
    1257 # Is it a lower triangular, strictly lower triangular, or unit lower triangular matrix?
    1258 #
    1259 sub _IsLowerTriangularMatrix {
    1260   my($This, $DiagonalValue) = @_;
    1261
    1262   # Is is a square matrix?
    1263   if (!$This->IsSquare()) {
    1264     return 0;
    1265   }
    1266   # Check matrix values...
    1267   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $CheckDiagonalValues);
    1268
    1269   $CheckDiagonalValues = defined($DiagonalValue) ? 1 : 0;
    1270   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1271
    1272   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1273     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1274       if ($CheckDiagonalValues && $RowIndex == $ColIndex) {
    1275         # Main diagonal...
    1276         if ($This->{Values}[$RowIndex][$ColIndex] != $DiagonalValue) {
    1277           return 0;
    1278         }
    1279       }
    1280       elsif ($RowIndex < $ColIndex) {
    1281         # Elemens above the main diagonal...
    1282         if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
    1283           return 0;
    1284         }
    1285       }
    1286     }
    1287   }
    1288   return 1;
    1289 }
    1290
    1291 # Is it an upper triangular matrix?
    1292 #
    1293 # A matrix is an upper triangular matrix:
    1294 #   o It's a square matrix
    1295 #   o All its entries below the main diagonal are zero
    1296 #
    1297 sub IsUpperTriangular {
    1298   my($This) = @_;
    1299
    1300   return $This->_IsUpperTriangularMatrix();
    1301 }
    1302
    1303 # Is it a right triangular matrix?
    1304 #
    1305 # A matrix is a right triangular matrix:
    1306 #   o It's a square matrix
    1307 #   o All its entries below the main diagonal are zero
    1308 #
    1309 sub IsRightTriangular {
    1310   my($This) = @_;
    1311
    1312   return $This->IsUpperTriangular();
    1313 }
    1314
    1315 # Is it a strictly upper triangular matrix?
    1316 #
    1317 # A matrix is a strictly upper triangular matrix:
    1318 #   o It's a square matrix
    1319 #   o All its entries on and below the main diagonal are zero
    1320 #
    1321 sub IsStrictlyUpperTriangular {
    1322   my($This) = @_;
    1323   my($DiagonalValue);
    1324
    1325   $DiagonalValue = 0;
    1326
    1327   return $This->_IsUpperTriangularMatrix($DiagonalValue);
    1328 }
    1329
    1330 # Is it a unit upper triangular matrix?
    1331 #
    1332 # A matrix is an unit upper triangular matrix:
    1333 #   o It's a square matrix
    1334 #   o All its entries main diagonal are one
    1335 #   o All its entries below the main diagonal are zero
    1336 #
    1337 sub IsUnitUpperTriangular {
    1338   my($This) = @_;
    1339   my($DiagonalValue);
    1340
    1341   $DiagonalValue = 1;
    1342
    1343   return $This->_IsUpperTriangularMatrix($DiagonalValue);
    1344 }
    1345
    1346 # Is it a upper unitriangular matrix?
    1347 #
    1348 sub IsUpperUniTriangular {
    1349   my($This) = @_;
    1350
    1351   return $This->IsUnitUpperTriangular();
    1352 }
    1353
    1354 # Is it an upper triangular, strictly upper triangular, or unit upper triangular matrix?
    1355 #
    1356 sub _IsUpperTriangularMatrix {
    1357   my($This, $DiagonalValue) = @_;
    1358
    1359   # Is is a square matrix?
    1360   if (!$This->IsSquare()) {
    1361     return 0;
    1362   }
    1363   # Check matrix values...
    1364   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $CheckDiagonalValues);
    1365
    1366   $CheckDiagonalValues = defined($DiagonalValue) ? 1 : 0;
    1367   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1368
    1369   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1370     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1371       if ($CheckDiagonalValues && $RowIndex == $ColIndex) {
    1372         # Main diagonal...
    1373         if ($This->{Values}[$RowIndex][$ColIndex] != $DiagonalValue) {
    1374           return 0;
    1375         }
    1376       }
    1377       elsif ($RowIndex > $ColIndex) {
    1378         # Elemens below the main diagonal...
    1379         if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
    1380           return 0;
    1381         }
    1382       }
    1383     }
    1384   }
    1385   return 1;
    1386 }
    1387
    1388 # Is it a symmetrix matrix?
    1389 #
    1390 # A matrix is a symmetric matrix:
    1391 #   o It's a square matrix
    1392 #   o Its elements are symmetric with respect to main diagonal. In other words,
    1393 #     elements below the main diagonal are equal to the elements above the main
    1394 #     diagonal.
    1395 #
    1396 # Transpose of a symmetric matrix equals the matrix itself.
    1397 #
    1398 sub IsSymmetric {
    1399   my($This) = @_;
    1400
    1401   # Is is a square matrix?
    1402   if (!$This->IsSquare()) {
    1403     return 0;
    1404   }
    1405
    1406   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1407   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1408
    1409   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1410     for $ColIndex (0 .. ($RowIndex - 1)) {
    1411       if ($This->{Values}[$RowIndex][$ColIndex] != $This->{Values}[$ColIndex][$RowIndex]) {
    1412         return 0;
    1413       }
    1414     }
    1415   }
    1416   return 1;
    1417 }
    1418
    1419 # Is it a anti symmetrix matrix?
    1420 #
    1421 # A matrix is an anti symmetric matrix:
    1422 #   o It's a square matrix
    1423 #   o Its elements are asymmetric with respect to main diagonal. In other words,
    1424 #     elements below the main diagonal are equal to the negative of elements above
    1425 #     the main diagonal.
    1426 #
    1427 # Transpose of a anti symmetric matrix equals the negative of the matrix.
    1428 #
    1429 sub IsAntiSymmetric {
    1430   my($This) = @_;
    1431
    1432   # Is is a square matrix?
    1433   if (!$This->IsSquare()) {
    1434     return 0;
    1435   }
    1436
    1437   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1438   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1439
    1440   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1441     for $ColIndex (0 .. ($RowIndex - 1)) {
    1442       if ($This->{Values}[$RowIndex][$ColIndex] != -$This->{Values}[$ColIndex][$RowIndex]) {
    1443         return 0;
    1444       }
    1445     }
    1446   }
    1447   return 1;
    1448 }
    1449
    1450 # Is it a skew symmetrix matrix?
    1451 #
    1452 # It's another name for AnitSymmetricMatrix.
    1453 #
    1454 sub IsSkewSymmetric {
    1455   my($This) = @_;
    1456
    1457   return $This->IsAntiSymmetric();
    1458 }
    1459
    1460 # Is it a positive matrix with all its values >= zero?
    1461 #
    1462 sub IsPositive {
    1463   my($This) = @_;
    1464
    1465   # Check matrix values...
    1466   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1467   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1468
    1469   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1470     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1471       if ($This->{Values}[$RowIndex][$ColIndex] < 0.0) {
    1472         return 0;
    1473       }
    1474     }
    1475   }
    1476   return 1;
    1477 }
    1478
    1479 # Is it a positive matrix with all its values <= zero?
    1480 #
    1481 sub IsNegative {
    1482   my($This) = @_;
    1483
    1484   return $This->IsPositive() ? 0 : 1;
    1485 }
    1486
    1487 # Transpose the matrix by swaping rows with columns...
    1488 #
    1489 sub Transpose {
    1490   my($This) = @_;
    1491   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1492
    1493   # Create the transpose matrix of size $NumOfCols x $NumOfRows
    1494   #
    1495   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1496   $Matrix = new Matrix($NumOfCols, $NumOfRows);
    1497
    1498   # Swap rows and columns...
    1499   for $RowIndex (0 .. ($NumOfCols - 1)) {
    1500     for $ColIndex (0 .. ($NumOfRows - 1)) {
    1501       $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$ColIndex][$RowIndex];
    1502     }
    1503   }
    1504   return $Matrix;
    1505 }
    1506
    1507 # Is it a matrix object?
    1508 sub IsMatrix ($) {
    1509   my($Object) = @_;
    1510
    1511   return _IsMatrix($Object);
    1512 }
    1513
    1514 # Set value print format for an individual object or the whole class during StringifyMatrix operation...
    1515 sub SetValuePrintFormat ($;$) {
    1516   my($FirstParameter, $SecondParameter) = @_;
    1517
    1518   if ((@_ == 2) && (_IsMatrix($FirstParameter))) {
    1519     # Set value print format for the specific object...
    1520     my($This, $ValuePrintFormat) = ($FirstParameter, $SecondParameter);
    1521
    1522     $This->{ValueFormat} = $ValuePrintFormat;
    1523   }
    1524   else {
    1525     # Set value print format for the class...
    1526     my($ValuePrintFormat) = ($FirstParameter);
    1527
    1528     $ValueFormat = $ValuePrintFormat;
    1529   }
    1530 }
    1531
    1532 # Set print style for matrix rows for an individual object or the whole class during StringifyMatrix
    1533 # operation.
    1534 #
    1535 # Possible values: AllRowsInOneLine, OneRowPerLine. Default: AllRowsInOneLine
    1536 #
    1537 sub SetMatrixPrintStyle ($;$) {
    1538   my($FirstParameter, $SecondParameter) = @_;
    1539
    1540   if ((@_ == 2) && (_IsMatrix($FirstParameter))) {
    1541     # Set value print format for the specific object...
    1542     my($This, $MatrixPrintStyleValue) = ($FirstParameter, $SecondParameter);
    1543
    1544     if ($MatrixPrintStyleValue !~ /^(AllRowsInOneLine|OneRowPerLine)$/i) {
    1545       croak "Error: ${ClassName}->SetMatrixPrintStyle: Specified MatrixPrintStyle, $MatrixPrintStyleValue, is not valid. Supported values: AllRowsInOneLine, OneRowPerLine...";
    1546     }
    1547
    1548     $This->{MatrixPrintStyle} = $MatrixPrintStyleValue;
    1549   }
    1550   else {
    1551     # Set value print format for the class...
    1552     my($MatrixPrintStyleValue) = ($FirstParameter);
    1553
    1554     if ($MatrixPrintStyleValue !~ /^(AllRowsInOneLine|OneRowPerLine)$/i) {
    1555       croak "Error: ${ClassName}::SetMatrixPrintStyle: Specified MatrixPrintStyle, $MatrixPrintStyleValue, is not valid. Supported values: AllRowsInOneLine, OneRowPerLine...";
    1556     }
    1557
    1558     $MatrixPrintStyle = $MatrixPrintStyleValue;
    1559   }
    1560 }
    1561
    1562 # Is it a matrix object?
    1563 #
    1564 sub _IsMatrix {
    1565   my($Object) = @_;
    1566
    1567   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
    1568 }
    1569
    1570 # Make sure it's a matrix reference...
    1571 #
    1572 sub _ValidateMatrix {
    1573   my($ErrorMsg, $Matrix) = @_;
    1574
    1575   if (!_IsMatrix($Matrix)) {
    1576     croak "Error: ${ClassName}->${ErrorMsg}: Object must be a matrix...";
    1577   }
    1578 }
    1579
    1580 # Make sure both row and column indicies are valid...
    1581 #
    1582 sub _ValidateRowAndColumnIndicies {
    1583   my($This, $ErrorMsgPrefix, $RowIndex, $ColumnIndex) = @_;
    1584
    1585   $This->_ValidateRowIndex($ErrorMsgPrefix, $RowIndex);
    1586   $This->_ValidateColumnIndex($ErrorMsgPrefix, $ColumnIndex);
    1587
    1588   return $This;
    1589 }
    1590
    1591 # Make sure it's a valid row index...
    1592 #
    1593 sub _ValidateRowIndex {
    1594   my($This, $ErrorMsgPrefix, $RowIndex) = @_;
    1595   my($NumOfRows);
    1596
    1597   if (!defined $RowIndex) {
    1598     croak "$ErrorMsgPrefix: RowIndex must be defined...";
    1599   }
    1600   $NumOfRows = $This->GetNumOfRows();
    1601   if ($RowIndex < 0 || $RowIndex >= $NumOfRows) {
    1602     croak "$ErrorMsgPrefix: RowIndex value $RowIndex must be >= 0 and < $NumOfRows, NumOfRows, in matrix...";
    1603   }
    1604   return $This;
    1605 }
    1606
    1607 # Make sure it's a valid column index...
    1608 #
    1609 sub _ValidateColumnIndex {
    1610   my($This, $ErrorMsgPrefix, $ColIndex) = @_;
    1611   my($NumOfCols);
    1612
    1613   if (!defined $ColIndex) {
    1614     croak "$ErrorMsgPrefix: ColIndex must be defined...";
    1615   }
    1616   $NumOfCols = $This->GetNumOfColumns();
    1617   if ($ColIndex < 0 || $ColIndex >= $NumOfCols) {
    1618     croak "$ErrorMsgPrefix: ColIndex value $ColIndex must be >= 0 and < $NumOfCols, NumOfCols, in matrix...";
    1619   }
    1620   return $This;
    1621 }
    1622
    1623 #
    1624 # Matrix addition operator supports two addition modes:
    1625 #   . Addition of two matrices by adding corresponding matrix values
    1626 #   . Addition of a scalar value to matrix values ($Matrix + 1)
    1627 #
    1628 # Caveats:
    1629 #   . Addition of a matrix to scalar is not allowed (1 + $Matrix)
    1630 #
    1631 sub _MatrixAdditionOperator {
    1632   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1633
    1634   $ErrorMsg = "_MatrixAdditionOperator: Matrix addition failed";
    1635   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1636
    1637   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1638
    1639   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1640   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    1641
    1642   if ($OtherIsMatrix) {
    1643     # $OrderFlipped is set to false for two matrices...
    1644     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1645       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1646         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] + $Other->{Values}[$RowIndex][$ColIndex];
    1647       }
    1648     }
    1649   }
    1650   else {
    1651     # Scalar addition...
    1652     if ($OrderFlipped) {
    1653       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1654     }
    1655     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1656       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1657         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] + $Other;
    1658       }
    1659     }
    1660   }
    1661   return $Matrix;
    1662 }
    1663
    1664 #
    1665 # Matrix subtraction operator supports two subtraction modes:
    1666 #   . Subtraction of two matrices by subtracting corresponding matrix values
    1667 #   . Subtraction of a scalar value from matrix values ($Matrix - 1)
    1668 #
    1669 # Caveats:
    1670 #   . Subtraction of a matrix from scalar is not allowed (1 - $Matrix)
    1671 #
    1672 sub _MatrixSubtractionOperator {
    1673   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1674
    1675   $ErrorMsg = "_MatrixSubtractionOperator: Matrix subtraction failed";
    1676   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1677
    1678   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1679
    1680   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1681   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    1682
    1683   if ($OtherIsMatrix) {
    1684     # $OrderFlipped is set to false for two matrices...
    1685     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1686       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1687         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] - $Other->{Values}[$RowIndex][$ColIndex];
    1688       }
    1689     }
    1690   }
    1691   else {
    1692     # Scalar subtraction...
    1693     if ($OrderFlipped) {
    1694       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1695     }
    1696     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1697       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1698         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] - $Other;
    1699       }
    1700     }
    1701   }
    1702   return $Matrix;
    1703 }
    1704
    1705 #
    1706 # Matrix multiplication operator supports two multiplication modes:
    1707 #   . Multiplication of two matrices
    1708 #   . Multiplication of matrix values by a scalar ($Matrix * 1)
    1709 #
    1710 # Caveats:
    1711 #   . Multiplication of a scalar by a is not allowed (1 * $Matrix)
    1712 #
    1713 sub _MatrixMultiplicationOperator {
    1714   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg, $CheckSizes);
    1715
    1716   $ErrorMsg = "_MatrixMultiplicationOperator: Matrix multiplication failed";
    1717   $CheckSizes = 0;
    1718   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckSizes);
    1719
    1720   my($Matrix);
    1721
    1722   if ($OtherIsMatrix) {
    1723     # $OrderFlipped is set to false for two matrices...
    1724     my($NumOfRows1, $NumOfCols1, $RowIndex1, $ColIndex1, $NumOfRows2, $NumOfCols2, $ColIndex2, $Value, $RowColIndex);
    1725
    1726     ($NumOfRows1, $NumOfCols1) = $This->GetSize();
    1727     ($NumOfRows2, $NumOfCols2) = $Other->GetSize();
    1728
    1729     if ($NumOfCols1 != $NumOfRows2) {
    1730       croak "Error: ${ClassName}->${ErrorMsg}: NumOfCols in first matrix of size $NumOfRows1 x $NumOfCols1 must be equal to NumOfRows in second matrix of size $NumOfRows2 x $NumOfCols2...";
    1731     }
    1732
    1733     $Matrix = new Matrix($NumOfRows1, $NumOfCols2);
    1734
    1735     for $RowIndex1 (0 .. ($NumOfRows1 - 1)) {
    1736       for $ColIndex2 (0 .. ($NumOfCols2 - 1)) {
    1737         $Value = 0;
    1738         for $RowColIndex (0 .. ($NumOfCols1 - 1)) {
    1739           $Value += $This->{Values}[$RowIndex1][$RowColIndex] * $Other->[$RowColIndex][$ColIndex2];
    1740         }
    1741         $Matrix->{Values}[$RowIndex1][$ColIndex2] = $Value;
    1742       }
    1743     }
    1744   }
    1745   else {
    1746     my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1747
    1748     ($NumOfRows, $NumOfCols) = $This->GetSize();
    1749     $Matrix = new Matrix($NumOfRows, $NumOfCols);
    1750     # Scalar subtraction...
    1751     if ($OrderFlipped) {
    1752       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1753     }
    1754     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1755       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1756         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] * $Other;
    1757       }
    1758     }
    1759   }
    1760   return $Matrix;
    1761 }
    1762
    1763 #
    1764 # Matrix division operator supports two division modes:
    1765 #   . Division of two matrices by dividing corresponding matrix values
    1766 #   . Division matrix values  by a scalar($Matrix/2)
    1767 #
    1768 # Caveats:
    1769 #   . Division of scalar value by a matrix is not allowed (2/$Matrix)
    1770 #
    1771 sub _MatrixDivisionOperator {
    1772   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1773
    1774   $ErrorMsg = "_MatrixDivisionOperator: Matrix division failed";
    1775   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1776
    1777   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1778
    1779   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1780   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    1781
    1782   if ($OtherIsMatrix) {
    1783     # $OrderFlipped is set to false for two matrices...
    1784     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1785       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1786         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] / $Other->{Values}[$RowIndex][$ColIndex];
    1787       }
    1788     }
    1789   }
    1790   else {
    1791     # Scalar subtraction...
    1792     if ($OrderFlipped) {
    1793       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1794     }
    1795     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1796       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1797         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] / $Other;
    1798       }
    1799     }
    1800   }
    1801   return $Matrix;
    1802 }
    1803
    1804 #
    1805 # Matrix exponentiation operator supports two division modes:
    1806 #   . Exponent of two matrices by exponentiation of corresponding matrix values
    1807 #   . Exponentiation matrix values  by a scalar ($Matrix ** 2)
    1808 #
    1809 # Caveats:
    1810 #   . Exponentiation of scalar value by a matrix is not allowed (2 ** $Matrix)
    1811 #
    1812 sub _MatrixExponentiationOperator {
    1813   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1814
    1815   $ErrorMsg = "_MatrixExponentiationOperator: Matrix exponentiation failed";
    1816   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1817
    1818   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1819
    1820   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1821   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    1822
    1823   if ($OtherIsMatrix) {
    1824     # $OrderFlipped is set to false for two matrices...
    1825     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1826       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1827         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] ** $Other->{Values}[$RowIndex][$ColIndex];
    1828       }
    1829     }
    1830   }
    1831   else {
    1832     # Scalar subtraction...
    1833     if ($OrderFlipped) {
    1834       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1835     }
    1836     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1837       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1838         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] ** $Other;
    1839       }
    1840     }
    1841   }
    1842   return $Matrix;
    1843 }
    1844
    1845 #
    1846 # Matrix modulus operator supports two division modes:
    1847 #   . Modulus of two matrices by taking modulus between corresponding matrix values
    1848 #   . Modulus of matrix values  by a scalar ($Matrix % 2)
    1849 #
    1850 # Caveats:
    1851 #   . Modulus of scalar value by a matrix is not allowed (2 % $Matrix)
    1852 #
    1853 sub _MatrixModulusOperator {
    1854   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1855
    1856   $ErrorMsg = "_MatrixModulusOperator: Matrix modulus failed";
    1857   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1858
    1859   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1860
    1861   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1862   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    1863
    1864   if ($OtherIsMatrix) {
    1865     # $OrderFlipped is set to false for two matrices...
    1866     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1867       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1868         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] % $Other->{Values}[$RowIndex][$ColIndex];
    1869       }
    1870     }
    1871   }
    1872   else {
    1873     # Scalar subtraction...
    1874     if ($OrderFlipped) {
    1875       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1876     }
    1877     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1878       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1879         $Matrix->{Values}[$RowIndex][$ColIndex] = $This->{Values}[$RowIndex][$ColIndex] % $Other;
    1880       }
    1881     }
    1882   }
    1883   return $Matrix;
    1884 }
    1885
    1886 #
    1887 # Matrix booelan operator checks whether a matrix contains at least one non-zero
    1888 # value...
    1889 #
    1890 sub _MatrixBooleanOperator {
    1891   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1892
    1893   $ErrorMsg = "_MatrixBooleanOperator: Matrix boolean operation failed";
    1894   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1895
    1896   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1897
    1898   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1899   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1900     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1901       if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
    1902         return 1;
    1903       }
    1904     }
    1905   }
    1906   return 0;
    1907 }
    1908
    1909 #
    1910 # Matrix not booelan operator checks whether a matrix contains only zero values...
    1911 # value...
    1912 #
    1913 sub _MatrixNotBooleanOperator {
    1914   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    1915
    1916   $ErrorMsg = "_MatrixNotBooleanOperator: Matrix not boolean operation failed";
    1917   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    1918
    1919   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1920
    1921   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1922   for $RowIndex (0 .. ($NumOfRows - 1)) {
    1923     for $ColIndex (0 .. ($NumOfCols - 1)) {
    1924       if ($This->{Values}[$RowIndex][$ColIndex] != 0.0) {
    1925         return 0;
    1926       }
    1927     }
    1928   }
    1929   return 1;
    1930 }
    1931
    1932 #
    1933 # Matrix equal operator supports two modes:
    1934 #   . Comparison of corresponding values in two matrices
    1935 #   . Comparing matrix values to a scalar ($Matrix == 2)
    1936 #
    1937 # Caveats:
    1938 #   . Comparison of a scalar to matrix values is not allowed (2 == $Matrix)
    1939 #
    1940 sub _MatrixEqualOperator {
    1941   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);
    1942
    1943   $ErrorMsg = "_MatrixEqualOperator: Matrix equal failed";
    1944   $CheckMatrixSizes = 0;
    1945   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);
    1946
    1947   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    1948
    1949   ($NumOfRows, $NumOfCols) = $This->GetSize();
    1950
    1951   if ($OtherIsMatrix) {
    1952     # $OrderFlipped is set to false for two matrices...
    1953     my($OtherNumOfRows, $OtherNumOfCols);
    1954
    1955     # Check sizes...
    1956     ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    1957     if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
    1958       return 0;
    1959     }
    1960
    1961     # Check values...
    1962     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1963       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1964         if ($This->{Values}[$RowIndex][$ColIndex] != $Other->{Values}[$RowIndex][$ColIndex]) {
    1965           return 0;
    1966         }
    1967       }
    1968     }
    1969   }
    1970   else {
    1971     # Scalar comparison...
    1972     if ($OrderFlipped) {
    1973       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    1974     }
    1975     for $RowIndex (0 .. ($NumOfRows - 1)) {
    1976       for $ColIndex (0 .. ($NumOfCols - 1)) {
    1977         if ($This->{Values}[$RowIndex][$ColIndex] != $Other) {
    1978           return 0;
    1979         }
    1980       }
    1981     }
    1982   }
    1983   return 1;
    1984 }
    1985
    1986 #
    1987 # Matrix not equal operator supports two modes:
    1988 #   . Comparison of corresponding values in two matrices
    1989 #   . Comparing matrix values to a scalar ($Matrix != 2)
    1990 #
    1991 # Caveats:
    1992 #   . Comparison of a scalar to matrix values is not allowed (2 != $Matrix)
    1993 #
    1994 sub _MatrixNotEqualOperator {
    1995   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);
    1996
    1997   $ErrorMsg = "_MatrixNotEqualOperator: Matrix not equal failed";
    1998   $CheckMatrixSizes = 0;
    1999   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);
    2000
    2001   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2002
    2003   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2004
    2005   if ($OtherIsMatrix) {
    2006     # $OrderFlipped is set to false for two matrices...
    2007     my($OtherNumOfRows, $OtherNumOfCols);
    2008
    2009     # Check sizes...
    2010     ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    2011     if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
    2012       return 1;
    2013     }
    2014
    2015     # Check values...
    2016     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2017       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2018         if ($This->{Values}[$RowIndex][$ColIndex] == $Other->{Values}[$RowIndex][$ColIndex]) {
    2019           return 0;
    2020         }
    2021       }
    2022     }
    2023   }
    2024   else {
    2025     # Scalar comparison...
    2026     if ($OrderFlipped) {
    2027       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    2028     }
    2029     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2030       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2031         if ($This->{Values}[$RowIndex][$ColIndex] == $Other) {
    2032           return 0;
    2033         }
    2034       }
    2035     }
    2036   }
    2037   return 1;
    2038 }
    2039
    2040 #
    2041 # Matrix less than operator supports two modes:
    2042 #   . Comparison of corresponding values in two matrices
    2043 #   . Comparing matrix values to a scalar ($Matrix < 2)
    2044 #
    2045 # Caveats:
    2046 #   . Comparison of a scalar to matrix values is not allowed (2 < $Matrix)
    2047 #
    2048 sub _MatrixLessThanOperator {
    2049   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);
    2050
    2051   $ErrorMsg = "_MatrixLessThanOperator: Matrix less than failed";
    2052   $CheckMatrixSizes = 0;
    2053   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);
    2054
    2055   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2056
    2057   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2058
    2059   if ($OtherIsMatrix) {
    2060     # $OrderFlipped is set to false for two matrices...
    2061     my($OtherNumOfRows, $OtherNumOfCols);
    2062
    2063     # Check sizes...
    2064     ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    2065     if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
    2066       return 0;
    2067     }
    2068
    2069     # Check values...
    2070     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2071       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2072         if ($This->{Values}[$RowIndex][$ColIndex] >= $Other->{Values}[$RowIndex][$ColIndex]) {
    2073           return 0;
    2074         }
    2075       }
    2076     }
    2077   }
    2078   else {
    2079     # Scalar comparison...
    2080     if ($OrderFlipped) {
    2081       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    2082     }
    2083     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2084       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2085         if ($This->{Values}[$RowIndex][$ColIndex] >= $Other) {
    2086           return 0;
    2087         }
    2088       }
    2089     }
    2090   }
    2091   return 1;
    2092 }
    2093
    2094 #
    2095 # Matrix less than equal operator supports two modes:
    2096 #   . Comparion of corresponding values in two matrices
    2097 #   . Comparing matrix values to a scalar ($Matrix <= 2)
    2098 #
    2099 # Caveats:
    2100 #   . Comparison of a scalar to matrix values is not allowed (2 <= $Matrix)
    2101 #
    2102 sub _MatrixLessThanEqualOperator {
    2103   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);
    2104
    2105   $ErrorMsg = "_MatrixLessThanEqualOperator: Matrix less than equal failed";
    2106   $CheckMatrixSizes = 0;
    2107   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);
    2108
    2109   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2110
    2111   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2112
    2113   if ($OtherIsMatrix) {
    2114     # $OrderFlipped is set to false for two matrices...
    2115     my($OtherNumOfRows, $OtherNumOfCols);
    2116
    2117     # Check sizes...
    2118     ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    2119     if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
    2120       return 0;
    2121     }
    2122
    2123     # Check values...
    2124     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2125       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2126         if ($This->{Values}[$RowIndex][$ColIndex] > $Other->{Values}[$RowIndex][$ColIndex]) {
    2127           return 0;
    2128         }
    2129       }
    2130     }
    2131   }
    2132   else {
    2133     # Scalar comparison...
    2134     if ($OrderFlipped) {
    2135       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    2136     }
    2137     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2138       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2139         if ($This->{Values}[$RowIndex][$ColIndex] > $Other) {
    2140           return 0;
    2141         }
    2142       }
    2143     }
    2144   }
    2145   return 1;
    2146 }
    2147
    2148 #
    2149 # Matrix greatar than operator supports two modes:
    2150 #   . Comparison of corresponding values in two matrices
    2151 #   . Comparing matrix values to a scalar ($Matrix > 2)
    2152 #
    2153 # Caveats:
    2154 #   . Comparison of a scalar to matrix values is not allowed (2 > $Matrix)
    2155 #
    2156 sub _MatrixGreatarThanOperator {
    2157   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);
    2158
    2159   $ErrorMsg = "_MatrixGreatarThanOperator: Matrix greatar than failed";
    2160   $CheckMatrixSizes = 0;
    2161   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);
    2162
    2163   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2164
    2165   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2166
    2167   if ($OtherIsMatrix) {
    2168     # $OrderFlipped is set to false for two matrices...
    2169     my($OtherNumOfRows, $OtherNumOfCols);
    2170
    2171     # Check sizes...
    2172     ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    2173     if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
    2174       return 0;
    2175     }
    2176
    2177     # Check values...
    2178     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2179       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2180         if ($This->{Values}[$RowIndex][$ColIndex] <= $Other->{Values}[$RowIndex][$ColIndex]) {
    2181           return 0;
    2182         }
    2183       }
    2184     }
    2185   }
    2186   else {
    2187     # Scalar comparison...
    2188     if ($OrderFlipped) {
    2189       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    2190     }
    2191     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2192       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2193         if ($This->{Values}[$RowIndex][$ColIndex] <= $Other) {
    2194           return 0;
    2195         }
    2196       }
    2197     }
    2198   }
    2199   return 1;
    2200 }
    2201
    2202 #
    2203 # Matrix greatar than equal operator supports two modes:
    2204 #   . Comparison of corresponding values in two matrices
    2205 #   . Comparing matrix values to a scalar ($Matrix >= 2)
    2206 #
    2207 # Caveats:
    2208 #   . Comparison of a scalar to matrix values is not allowed (2 >= $Matrix)
    2209 #
    2210 sub _MatrixGreatarThanEqualOperator {
    2211   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes, $ErrorMsg);
    2212
    2213   $ErrorMsg = "_MatrixGreatarThanEqualOperator: Matrix greatar than equal failed";
    2214   $CheckMatrixSizes = 0;
    2215   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckMatrixSizes);
    2216
    2217   my($NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2218
    2219   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2220
    2221   if ($OtherIsMatrix) {
    2222     # $OrderFlipped is set to false for two matrices...
    2223     my($OtherNumOfRows, $OtherNumOfCols);
    2224
    2225     # Check sizes...
    2226     ($OtherNumOfRows, $OtherNumOfCols) = $Other->GetSize();
    2227     if (!($NumOfRows == $OtherNumOfRows && $NumOfCols == $OtherNumOfCols)) {
    2228       return 0;
    2229     }
    2230
    2231     # Check values...
    2232     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2233       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2234         if ($This->{Values}[$RowIndex][$ColIndex] < $Other->{Values}[$RowIndex][$ColIndex]) {
    2235           return 0;
    2236         }
    2237       }
    2238     }
    2239   }
    2240   else {
    2241     # Scalar comparison...
    2242     if ($OrderFlipped) {
    2243       croak "Error: ${ClassName}->${ErrorMsg}: First object must be a matrix...";
    2244     }
    2245     for $RowIndex (0 .. ($NumOfRows - 1)) {
    2246       for $ColIndex (0 .. ($NumOfCols - 1)) {
    2247         if ($This->{Values}[$RowIndex][$ColIndex] < $Other) {
    2248           return 0;
    2249         }
    2250       }
    2251     }
    2252   }
    2253   return 1;
    2254 }
    2255
    2256 #
    2257 # Matrix negative value operator returns a matrix with values corresponding to
    2258 # negative values of a matrix
    2259 #
    2260 sub _MatrixNegativeValueOperator {
    2261   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2262
    2263   $ErrorMsg = "_MatrixNegativeValueOperator: Matrix negative value operation failed";
    2264   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2265
    2266   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2267
    2268   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2269   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2270
    2271   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2272     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2273       $Matrix->{Values}[$RowIndex][$ColIndex] = - $This->{Values}[$RowIndex][$ColIndex];
    2274     }
    2275   }
    2276   return $Matrix;
    2277 }
    2278
    2279 #
    2280 # Matrix absolute value operator returns a matrix with values corresponding to
    2281 # absolute values of a matrix
    2282 #
    2283 sub _MatrixAbsoluteValueOperator {
    2284   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2285
    2286   $ErrorMsg = "_MatrixAbsoluteValueOperator: Matrix absolute value operation failed";
    2287   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2288
    2289   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2290
    2291   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2292   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2293
    2294   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2295     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2296       $Matrix->{Values}[$RowIndex][$ColIndex] = abs $This->{Values}[$RowIndex][$ColIndex];
    2297     }
    2298   }
    2299   return $Matrix;
    2300 }
    2301
    2302 #
    2303 # Matrix exp natural base operator returns a matrix with values corresponding to
    2304 # e raised to the power of values in a matrix
    2305 #
    2306 sub _MatrixExpNaturalBaseOperator {
    2307   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2308
    2309   $ErrorMsg = "_MatrixExpNaturalBaseOperator: Matrix exp operation failed";
    2310   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2311
    2312   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2313
    2314   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2315   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2316
    2317   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2318     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2319       $Matrix->{Values}[$RowIndex][$ColIndex] = exp $This->{Values}[$RowIndex][$ColIndex];
    2320     }
    2321   }
    2322   return $Matrix;
    2323 }
    2324
    2325 #
    2326 # Matrix log natural base operator returns a matrix with values corresponding to
    2327 # log of values in a matrix
    2328 #
    2329 sub _MatrixLogNaturalBaseOperator {
    2330   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2331
    2332   $ErrorMsg = "_MatrixLogNaturalBaseOperator: Matrix log operation failed";
    2333   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2334
    2335   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2336
    2337   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2338   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2339
    2340   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2341     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2342       $Matrix->{Values}[$RowIndex][$ColIndex] = log $This->{Values}[$RowIndex][$ColIndex];
    2343     }
    2344   }
    2345   return $Matrix;
    2346 }
    2347
    2348 #
    2349 # Matrix square root operator returns a matrix with values corresponding to
    2350 # sqrt of values in a matrix
    2351 #
    2352 sub _MatrixSquareRootOperator {
    2353   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2354
    2355   $ErrorMsg = "_MatrixSquareRootOperator: Matrix sqrt operation failed";
    2356   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2357
    2358   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2359
    2360   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2361   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2362
    2363   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2364     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2365       $Matrix->{Values}[$RowIndex][$ColIndex] = sqrt $This->{Values}[$RowIndex][$ColIndex];
    2366     }
    2367   }
    2368   return $Matrix;
    2369 }
    2370
    2371 #
    2372 # Matrix sine root operator returns a matrix with values corresponding to
    2373 # sin of values in a matrix
    2374 #
    2375 sub _MatrixSineOperator {
    2376   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2377
    2378   $ErrorMsg = "_MatrixSineOperator: Matrix sin operation failed";
    2379   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2380
    2381   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2382
    2383   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2384   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2385
    2386   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2387     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2388       $Matrix->{Values}[$RowIndex][$ColIndex] = sin $This->{Values}[$RowIndex][$ColIndex];
    2389     }
    2390   }
    2391   return $Matrix;
    2392 }
    2393
    2394 #
    2395 # Matrix cosine root operator returns a matrix with values corresponding to
    2396 # cos of values in a matrix
    2397 #
    2398 sub _MatrixCosineOperator {
    2399   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $ErrorMsg);
    2400
    2401   $ErrorMsg = "_MatrixCosineOperator: Matrix cos operation failed";
    2402   ($This, $Other, $OrderFlipped, $OtherIsMatrix) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_);
    2403
    2404   my($Matrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex);
    2405
    2406   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2407   $Matrix = new Matrix($NumOfRows, $NumOfCols);
    2408
    2409   for $RowIndex (0 .. ($NumOfRows - 1)) {
    2410     for $ColIndex (0 .. ($NumOfCols - 1)) {
    2411       $Matrix->{Values}[$RowIndex][$ColIndex] = cos $This->{Values}[$RowIndex][$ColIndex];
    2412     }
    2413   }
    2414   return $Matrix;
    2415 }
    2416
    2417 # Turn matrix into array for @{$Matrix} operation...
    2418 #
    2419 sub _MatrixToArrayOperator {
    2420   my($This) = @_;
    2421
    2422   return \@{$This->{Values}};
    2423 }
    2424
    2425 # Always return true in boolean context...
    2426 #
    2427 sub _BoolifyMatrix {
    2428   my($This) = @_;
    2429
    2430   return 1;
    2431 }
    2432
    2433 # Process parameters passed to overloaded operators...
    2434 #
    2435 # For uninary operators, $SecondParameter is not defined.
    2436 sub _ProcessOverloadedOperatorParameters {
    2437   my($ErrorMsg, $FirstParameter, $SecondParameter, $ParametersOrderStatus, $CheckMatrixSizesStatus) = @_;
    2438   my($This, $Other, $OrderFlipped, $OtherIsMatrix, $CheckMatrixSizes);
    2439
    2440   ($This, $Other) =  ($FirstParameter, $SecondParameter);
    2441   $OrderFlipped = (defined($ParametersOrderStatus) && $ParametersOrderStatus) ? 1 : 0;
    2442   $CheckMatrixSizes = (defined $CheckMatrixSizesStatus) ? $CheckMatrixSizesStatus : 1;
    2443
    2444   _ValidateMatrix($ErrorMsg, $This);
    2445
    2446   $OtherIsMatrix = 0;
    2447   if (defined($Other) && (ref $Other)) {
    2448     # Make sure $Other is a matrix...
    2449     _ValidateMatrix($ErrorMsg, $Other);
    2450     if ($CheckMatrixSizes) {
    2451       _ValidateMatrixSizesAreEqual($ErrorMsg, $This, $Other);
    2452     }
    2453     $OtherIsMatrix = 1;
    2454   }
    2455   return ($This, $Other, $OrderFlipped, $OtherIsMatrix);
    2456 }
    2457
    2458 # Make sure size of the two matrices contain the same number of values...
    2459 sub _ValidateMatrixSizesAreEqual {
    2460   my($ErrorMsg, $Matrix1, $Matrix2) = @_;
    2461   my($NumOfRows1, $NumOfCols1, $NumOfRows2, $NumOfCols2);
    2462
    2463   ($NumOfRows1, $NumOfCols1) = $Matrix1->GetSize();
    2464   ($NumOfRows2, $NumOfCols2) = $Matrix2->GetSize();
    2465
    2466   if (!($NumOfRows1 == $NumOfRows2 && $NumOfCols1 == $NumOfCols2)) {
    2467     croak "Error: ${ClassName}->${ErrorMsg}: Size of the matrices must be same...";
    2468   }
    2469 }
    2470
    2471 # Return a string containing matrix values...
    2472 #
    2473 sub StringifyMatrix {
    2474   my($This) = @_;
    2475   my($MatrixString, $MatrixPrintStyleValue, $PrintFormat, $AllRowsInOneLine, $FormatString, $NumOfRows, $NumOfCols, $RowIndex, $RowNum, $RowString, @ValuesFormat);
    2476
    2477   ($NumOfRows, $NumOfCols) = $This->GetSize();
    2478
    2479   $MatrixPrintStyleValue = (exists $This->{MatrixPrintStyle}) ? $This->{MatrixPrintStyle} : $MatrixPrintStyle;
    2480   $AllRowsInOneLine = ($MatrixPrintStyleValue =~ /^AllRowsInOneLine$/i) ? 1 : 0;
    2481
    2482   $PrintFormat = (exists $This->{ValueFormat}) ? $This->{ValueFormat} : $ValueFormat;
    2483
    2484   @ValuesFormat = ($PrintFormat) x $NumOfCols;
    2485   $FormatString = join ' ', @ValuesFormat;
    2486
    2487   $MatrixString = sprintf "<Size: $NumOfRows x $NumOfCols;";
    2488   if ($AllRowsInOneLine) {
    2489     $MatrixString .= sprintf " Values:";
    2490   }
    2491   else {
    2492     $MatrixString .= sprintf " Values:\n";
    2493   }
    2494
    2495   $RowNum = 0;
    2496   for $RowIndex (0 .. ($NumOfRows -1)) {
    2497     $RowNum++;
    2498     $RowString = sprintf "Row${RowNum}:[$FormatString]", @{$This->{Values}[$RowIndex]};
    2499     if ($AllRowsInOneLine) {
    2500       $MatrixString .= " $RowString";
    2501     }
    2502     else {
    2503       $MatrixString .= "$RowString\n";
    2504     }
    2505   }
    2506   $MatrixString .= ">";
    2507   return $MatrixString;
    2508 }
    2509