MayaChemTools

   1 package Matrix;
   2 #
   3 # File: Matrix.pm
   4 # Author: Manish Sud <msud@san.rr.com>
   5 #
   6 # Copyright (C) 2024 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