1 package Vector; 2 # 3 # File: Vector.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 StatisticsUtil (); 31 32 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 33 34 @ISA = qw(Exporter); 35 @EXPORT = qw(IsVector UnitXVector UnitYVector UnitZVector UnitVector ZeroVector); 36 @EXPORT_OK = qw(SetValuePrintFormat); 37 38 %EXPORT_TAGS = ( 39 all => [@EXPORT, @EXPORT_OK] 40 ); 41 42 # Setup class variables... 43 my($ClassName, $ValueFormat); 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 '""' => 'StringifyVector', 57 58 '0+' => '_NumifyVector', 59 60 '@{}' => '_VectorToArrayOperator', 61 62 '+' => '_VectorAdditionOperator', 63 '-' => '_VectorSubtractionOperator', 64 '*' => '_VectorMultiplicationOperator', 65 '/' => '_VectorDivisionOperator', 66 '**' => '_VectorExponentiationOperator', 67 '%' => '_VectorModulusOperator', 68 69 'x' => '_VectorCrossProductOperator', 70 '.' => '_VectorDotProductOperator', 71 72 'bool' => '_VectorBooleanOperator', 73 '!' => '_VectorNotBooleanOperator', 74 75 '==' => '_VectorEqualOperator', 76 '!=' => '_VectorNotEqualOperator', 77 '<' => '_VectorLessThanOperator', 78 '<=' => '_VectorLessThanEqualOperator', 79 '>' => '_VectorGreatarThanOperator', 80 '>=' => '_VectorGreatarThanEqualOperator', 81 82 'neg' => '_VectorNegativeValueOperator', 83 84 'abs' => '_VectorAbsoluteValueOperator', 85 'exp' => '_VectorExpNaturalBaseOperator', 86 'log' => '_VectorLogNaturalBaseOperator', 87 'sqrt' => '_VectorSquareRootOperator', 88 'cos' => '_VectorCosineOperator', 89 'sin' => '_VectorSineOperator', 90 91 'fallback' => undef; 92 93 # Class constructor... 94 sub new { 95 my($Class, @Values) = @_; 96 97 # Initialize object... 98 my $This = {}; 99 bless $This, ref($Class) || $Class; 100 $This->_InitializeVector(); 101 102 $This->_AddValues(@Values); 103 104 return $This; 105 } 106 107 # Initialize object data... 108 # 109 sub _InitializeVector { 110 my($This) = @_; 111 112 @{$This->{Values}} = (); 113 } 114 115 # Initialize class ... 116 sub _InitializeClass { 117 #Class name... 118 $ClassName = __PACKAGE__; 119 120 # Print format for vector values... 121 $ValueFormat = "%g"; 122 } 123 124 # Initialize vector values using: 125 # o List of values 126 # o Reference to an list of values 127 # o Another vector object 128 # 129 sub _AddValues { 130 my($This, @Values) = @_; 131 132 if (!@Values) { 133 return; 134 } 135 136 # Set vector values... 137 my($FirstValue, $TypeOfFirstValue); 138 $FirstValue = $Values[0]; 139 $TypeOfFirstValue = ref $FirstValue; 140 if ($TypeOfFirstValue =~ /^(SCALAR|HASH|CODE|REF|GLOB)/) { 141 croak "Error: ${ClassName}->_AddValues: Trying to add values to vector object with a reference to unsupported value format..."; 142 } 143 144 if (_IsVector($FirstValue)) { 145 # Initialize using Vector... 146 push @{$This->{Values}}, @{$FirstValue->{Values}}; 147 } 148 elsif ($TypeOfFirstValue =~ /^ARRAY/) { 149 # Initialize using array refernce... 150 push @{$This->{Values}}, @{$FirstValue}; 151 } 152 else { 153 # It's a list of values... 154 push @{$This->{Values}}, @Values; 155 } 156 } 157 158 # Add values to a vector using a vector, reference to an array or an array... 159 sub AddValues { 160 my($This, @Values) = @_; 161 162 $This->_AddValues(@Values); 163 164 return $This; 165 } 166 167 # Copy vector... 168 sub Copy { 169 my($This) = @_; 170 my($Vector); 171 172 # Copy vector values... 173 $Vector = (ref $This)->new(\@{$This->{Values}}); 174 175 # Copy value format for stringification... 176 if (exists $This->{ValueFormat}) { 177 $Vector->{ValueFormat} = $This->{ValueFormat}; 178 } 179 return $Vector; 180 } 181 182 # Get 3D vector length... 183 sub GetLength { 184 my($This) = @_; 185 186 if ($This->GetSize() != 3) { 187 croak "Error: ${ClassName}->GetGetLength: Object must be a 3D vector..."; 188 } 189 my($Length, $DotProduct); 190 $DotProduct = $This . $This; 191 $Length = sqrt $DotProduct; 192 193 return $Length; 194 } 195 196 # Length of a 3D vector by another name... 197 sub GetMagnitude { 198 my($This) = @_; 199 return $This->GetLength(); 200 } 201 202 # Normalize 3D vector... 203 sub Normalize { 204 my($This) = @_; 205 206 if ($This->GetSize() != 3) { 207 croak "Error: ${ClassName}->GetGetLength: Object must be a 3D vector..."; 208 } 209 my($Vector, $Length); 210 $Length = $This->GetLength(); 211 $Vector = $This / $Length; 212 213 return $Vector; 214 } 215 216 # Is it a vector object? 217 sub IsVector ($) { 218 my($Object) = @_; 219 220 return _IsVector($Object); 221 } 222 223 # Get size... 224 sub GetSize { 225 my($This) = @_; 226 227 return scalar @{$This->{Values}}; 228 } 229 230 # Get X value of a 3D vector... 231 sub GetX { 232 my($This) = @_; 233 234 if ($This->GetSize() != 3) { 235 croak "Error: ${ClassName}->GetX: Object must be a 3D vector..."; 236 } 237 return $This->_GetValue(0); 238 } 239 240 # Set X value of a 3D vector... 241 sub SetX { 242 my($This, $Value) = @_; 243 244 if ($This->GetSize() != 3) { 245 croak "Error: ${ClassName}->SetX: Object must be a 3D vector..."; 246 } 247 return $This->_SetValue(0, $Value); 248 } 249 250 # Get Y value of a 3D vector... 251 sub GetY { 252 my($This) = @_; 253 254 if ($This->GetSize() != 3) { 255 croak "Error: ${ClassName}->GetY: Object must be a 3D vector..."; 256 } 257 return $This->_GetValue(1); 258 } 259 260 # Set Y value of a 3D vector... 261 sub SetY { 262 my($This, $Value) = @_; 263 264 if ($This->GetSize() != 3) { 265 croak "Error: ${ClassName}->SetY: Object must be a 3D vector..."; 266 } 267 return $This->_SetValue(1, $Value); 268 } 269 270 # Get Z value of a 3D vector... 271 sub GetZ { 272 my($This) = @_; 273 274 if ($This->GetSize() != 3) { 275 croak "Error: ${ClassName}->GetZ: Object must be a 3D vector..."; 276 } 277 return $This->_GetValue(2); 278 } 279 280 # Set Z value of a 3D vector... 281 sub SetZ { 282 my($This, $Value) = @_; 283 284 if ($This->GetSize() != 3) { 285 croak "Error: ${ClassName}->SetZ: Object must be a 3D vector..."; 286 } 287 return $This->_SetValue(2, $Value); 288 } 289 290 # Set XYZ value of a 3D vector using: 291 # o List of values 292 # o Reference to an list of values 293 # o Another vector object 294 # 295 sub SetXYZ { 296 my($This, @Values) = @_; 297 298 if (!@Values) { 299 croak "Error: ${ClassName}->SetXYZ: No values specified..."; 300 } 301 302 if ($This->GetSize() != 3) { 303 croak "Error: ${ClassName}->SetXYZ: Object must be a 3D vector..."; 304 } 305 306 # Set vector values... 307 my($FirstValue, $TypeOfFirstValue); 308 $FirstValue = $Values[0]; 309 $TypeOfFirstValue = ref $FirstValue; 310 if ($TypeOfFirstValue =~ /^(SCALAR|HASH|CODE|REF|GLOB)/) { 311 croak "Error: ${ClassName}->SetXYZ: A reference to unsupported value format specified..."; 312 } 313 314 my($X, $Y, $Z); 315 if (_IsVector($FirstValue)) { 316 # SetXYZ using vector... 317 if ($FirstValue->GetSize() != 3) { 318 croak "Error: ${ClassName}->SetXYZ: Input object must be a 3D vector..."; 319 } 320 ($X, $Y, $Z) = @{$FirstValue->{Values}}; 321 } 322 elsif ($TypeOfFirstValue =~ /^ARRAY/) { 323 # SetXYZ using array reference... 324 if (@{$FirstValue} != 3) { 325 croak "Error: ${ClassName}->SetXYZ: Input array reference must correspond to an array with three values..."; 326 } 327 ($X, $Y, $Z) = @{$FirstValue}; 328 } 329 else { 330 # It's a list of values... 331 if (@Values != 3) { 332 croak "Error: ${ClassName}->SetXYZ: Input array must contain three values..."; 333 } 334 ($X, $Y, $Z) = @Values; 335 } 336 $This->{Values}[0] = $X; 337 $This->{Values}[1] = $Y; 338 $This->{Values}[2] = $Z; 339 340 return $This; 341 } 342 343 # Get XYZ as an array or a reference to an array... 344 # 345 sub GetXYZ { 346 my($This) = @_; 347 348 if ($This->GetSize() != 3) { 349 croak "Error: ${ClassName}->GetXYZ: Object must be a 3D vector..."; 350 } 351 return wantarray ? @{$This->{Values}} : \@{$This->{Values}}; 352 } 353 354 # Get a specific value from vector with indicies starting from 0.. 355 sub GetValue { 356 my($This, $Index) = @_; 357 358 if ($Index < 0) { 359 croak "Error: ${ClassName}->GetValue: Index value must be a positive number..."; 360 } 361 if ($Index >= $This->GetSize()) { 362 croak "Error: ${ClassName}->GetValue: Index value must be less than size of vector..."; 363 } 364 return $This->_GetValue($Index); 365 } 366 367 # Get a vector value... 368 sub _GetValue { 369 my($This, $Index) = @_; 370 371 return $This->{Values}[$Index]; 372 } 373 374 # Set a specific value in vector with indicies starting from 0.. 375 sub SetValue { 376 my($This, $Index, $Value, $SkipCheck) = @_; 377 378 # Just set it... 379 if ($SkipCheck) { 380 return $This->_SetValue($Index, $Value); 381 } 382 383 # Check and set... 384 if ($Index < 0) { 385 croak "Error: ${ClassName}->SetValue: Index value must be a positive number..."; 386 } 387 if ($Index >= $This->GetSize()) { 388 croak "Error: ${ClassName}->SetValue: Index vaue must be less than size of vector..."; 389 } 390 391 return $This->_SetValue($Index, $Value); 392 } 393 394 # Set a vector value... 395 sub _SetValue { 396 my($This, $Index, $Value) = @_; 397 398 $This->{Values}[$Index] = $Value; 399 400 return $This; 401 } 402 403 # Return vector values as an array or reference to an array... 404 sub GetValues { 405 my($This) = @_; 406 407 return wantarray ? @{$This->{Values}} : \@{$This->{Values}}; 408 } 409 410 # Get number of non-zero values in vector... 411 # 412 sub GetNumOfNonZeroValues { 413 my($This) = @_; 414 my($Count, $Index, $Size); 415 416 $Count = 0; 417 $Size = $This->GetSize(); 418 419 for $Index (0 .. ($Size -1)) { 420 if ($This->{Values}[$Index] != 0) { 421 $Count++; 422 } 423 } 424 return $Count; 425 } 426 427 # Get percent of non-zero values... 428 # 429 sub GetPercentOfNonZeroValues { 430 my($This) = @_; 431 432 return $This->GetSize() ? (($This->GetNumOfNonZeroValues()/$This->GetSize())*100) : 0; 433 } 434 435 # Set value print format for an individual object or the whole class... 436 sub SetValuePrintFormat ($;$) { 437 my($FirstParameter, $SecondParameter) = @_; 438 439 if ((@_ == 2) && (_IsVector($FirstParameter))) { 440 # Set value print format for the specific object... 441 my($This, $ValuePrintFormat) = ($FirstParameter, $SecondParameter); 442 443 $This->{ValueFormat} = $ValuePrintFormat; 444 } 445 else { 446 # Set value print format for the class... 447 my($ValuePrintFormat) = ($FirstParameter); 448 449 $ValueFormat = $ValuePrintFormat; 450 } 451 } 452 453 # Zero vector of specified size or size 3... 454 sub ZeroVector (;$) { 455 my($Size) = @_; 456 my($Vector, @Values); 457 458 $Size = (defined $Size) ? $Size : 3; 459 @Values = ('0') x $Size; 460 461 $Vector = new Vector(\@Values); 462 return $Vector; 463 } 464 465 # Unit vector of specified size or size 3... 466 sub UnitVector (;$) { 467 my($Size) = @_; 468 my($Vector, @Values); 469 470 $Size = (defined $Size) ? $Size : 3; 471 @Values = ('1') x $Size; 472 473 $Vector = new Vector(\@Values); 474 return $Vector; 475 } 476 477 # Unit X vector of size 3... 478 sub UnitXVector () { 479 my($Vector); 480 481 $Vector = new Vector(1, 0, 0); 482 return $Vector; 483 } 484 485 # Unit Y vector of size 3... 486 sub UnitYVector () { 487 my($Vector); 488 489 $Vector = new Vector(0, 1, 0); 490 return $Vector; 491 } 492 493 # Unit Z vector of size 3... 494 sub UnitZVector () { 495 my($Vector); 496 497 $Vector = new Vector(0, 0, 1); 498 return $Vector; 499 } 500 501 # 502 # Vector addition operator supports two addition modes: 503 # . Addition of two vectors by adding corresponding vector values 504 # . Addition of a scalar value to vector values ($Vector + 1) 505 # 506 # Caveats: 507 # . Addition of a vector to scalar is not allowed (1 + $Vector) 508 # 509 sub _VectorAdditionOperator { 510 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 511 512 $ErrorMsg = "_VectorAdditionOperator: Vector addition failed"; 513 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 514 515 # Do the addition. Order can be ignored: It's a commumative operation. 516 my($Vector, $ThisSize, $Index); 517 $Vector = $This->Copy(); 518 $ThisSize = $This->GetSize(); 519 520 if ($OtherIsVector) { 521 # $OrderFlipped is set to false for two vectors... 522 for $Index (0 .. ($ThisSize -1)) { 523 $Vector->{Values}[$Index] += $Other->{Values}[$Index]; 524 } 525 } 526 else { 527 if ($OrderFlipped) { 528 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 529 } 530 # Scalar addition... 531 for $Index (0 .. ($ThisSize -1)) { 532 $Vector->{Values}[$Index] += $Other; 533 } 534 } 535 return $Vector; 536 } 537 538 # 539 # Vector subtraction operator supports two subtraction modes: 540 # . Subtraction of two vectors by subtracting corresponding vector values 541 # . Subtraction of a scalar value from vector values ($Vector - 1) 542 # 543 # Caveats: 544 # . Subtraction of a vector from scalar is not allowed (1 - $Vector) 545 # 546 sub _VectorSubtractionOperator { 547 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 548 549 $ErrorMsg = "_VectorSubtractionOperator: Vector subtracttion failed"; 550 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 551 552 # Do the subtraction... 553 my($Vector, $ThisSize, $Index); 554 $Vector = $This->Copy(); 555 $ThisSize = $This->GetSize(); 556 557 if ($OtherIsVector) { 558 # $OrderFlipped is set to false for two vectors... 559 for $Index (0 .. ($ThisSize -1)) { 560 $Vector->{Values}[$Index] -= $Other->{Values}[$Index]; 561 } 562 } 563 else { 564 # Scalar subtraction... 565 if ($OrderFlipped) { 566 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 567 } 568 for $Index (0 .. ($ThisSize -1)) { 569 $Vector->{Values}[$Index] -= $Other; 570 } 571 } 572 return $Vector; 573 } 574 575 # 576 # Vector multiplication operator supports two multiplication modes: 577 # . Multiplication of two vectors by multiplying corresponding vector values 578 # . Multiplying vector values by a scalar ($Vector * 1) 579 # 580 # Caveats: 581 # . Multiplication of a scalar by a vector is not allowed (1 * $Vector) 582 # 583 sub _VectorMultiplicationOperator { 584 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 585 586 $ErrorMsg = "_VectorMultiplicationOperator: Vector addition failed"; 587 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 588 589 # Do the multiplication... 590 my($Vector, $ThisSize, $Index); 591 $Vector = $This->Copy(); 592 $ThisSize = $This->GetSize(); 593 594 if ($OtherIsVector) { 595 # $OrderFlipped is set to false for two vectors... 596 for $Index (0 .. ($ThisSize -1)) { 597 $Vector->{Values}[$Index] *= $Other->{Values}[$Index]; 598 } 599 } 600 else { 601 if ($OrderFlipped) { 602 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 603 } 604 # Scalar multiplication... 605 for $Index (0 .. ($ThisSize -1)) { 606 $Vector->{Values}[$Index] *= $Other; 607 } 608 } 609 return $Vector; 610 } 611 612 # 613 # Vector division operator supports two division modes: 614 # . Division of two vectors by dividing corresponding vector values 615 # . Dividing vector values by a scalar ($Vector / 2) 616 # 617 # Caveats: 618 # . Division of a scalar by a vector is not allowed (1 / $Vector) 619 # 620 sub _VectorDivisionOperator { 621 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 622 623 $ErrorMsg = "_VectorDivisionOperator: Vector division failed"; 624 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 625 626 # Do the division... 627 my($Vector, $ThisSize, $Index); 628 $Vector = $This->Copy(); 629 $ThisSize = $This->GetSize(); 630 631 if ($OtherIsVector) { 632 # $OrderFlipped is set to false for two vectors... 633 for $Index (0 .. ($ThisSize -1)) { 634 $Vector->{Values}[$Index] /= $Other->{Values}[$Index]; 635 } 636 } 637 else { 638 if ($OrderFlipped) { 639 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 640 } 641 # Scalar divison... 642 for $Index (0 .. ($ThisSize -1)) { 643 $Vector->{Values}[$Index] /= $Other; 644 } 645 } 646 return $Vector; 647 } 648 649 # 650 # Vector exponentiation operator supports two exponentiation modes: 651 # . Exponentiation of two vectors by exponentiation of corresponding vector values 652 # . Exponentiation of vector values by a scalar ($Vector ** 2) 653 # 654 # Caveats: 655 # . Exponent of scalar by a vector is not allowed (2 ** $Vector) 656 # 657 sub _VectorExponentiationOperator { 658 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 659 660 $ErrorMsg = "_VectorExponentiationOperator: Vector exponentiation failed"; 661 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 662 663 # Do the exponentiation... 664 my($Vector, $ThisSize, $Index); 665 $Vector = $This->Copy(); 666 $ThisSize = $This->GetSize(); 667 668 if ($OtherIsVector) { 669 # $OrderFlipped is set to false for two vectors... 670 for $Index (0 .. ($ThisSize -1)) { 671 $Vector->{Values}[$Index] **= $Other->{Values}[$Index]; 672 } 673 } 674 else { 675 if ($OrderFlipped) { 676 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 677 } 678 # Scalar exponentiation... 679 for $Index (0 .. ($ThisSize -1)) { 680 $Vector->{Values}[$Index] **= $Other; 681 } 682 } 683 return $Vector; 684 } 685 686 # 687 # Vector modulus operator supports two modulus modes: 688 # . Modulus of two vectors by taking modulus between corresponding vector values 689 # . Modulus of vector values by a scalar ($Vector % 2) 690 # 691 # Caveats: 692 # . Modulus of scalar by a vector is not allowed (2 % $Vector) 693 # 694 sub _VectorModulusOperator { 695 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 696 697 $ErrorMsg = "_VectorModulusOperator: Vector exponentiation failed"; 698 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 699 700 # Take the modulus... 701 my($Vector, $ThisSize, $Index); 702 $Vector = $This->Copy(); 703 $ThisSize = $This->GetSize(); 704 705 if ($OtherIsVector) { 706 # $OrderFlipped is set to false for two vectors... 707 for $Index (0 .. ($ThisSize -1)) { 708 $Vector->{Values}[$Index] %= $Other->{Values}[$Index]; 709 } 710 } 711 else { 712 if ($OrderFlipped) { 713 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 714 } 715 # Scalar modulus... 716 for $Index (0 .. ($ThisSize -1)) { 717 $Vector->{Values}[$Index] %= $Other; 718 } 719 } 720 return $Vector; 721 } 722 723 # 724 # Vector dot product operator supports two modes: 725 # . Dot product of two 3D vectors 726 # . Concatenation of a vector and a scalar 727 # 728 sub _VectorDotProductOperator { 729 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 730 731 $ErrorMsg = "_VectorDotProductOperator: Vector dot product failed"; 732 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 733 734 if ($OtherIsVector) { 735 # Calculate dot product of two 3D vectors... 736 my($DotProduct); 737 if ($This->GetSize() != 3) { 738 croak "Error: ${ClassName}->${ErrorMsg}: Both vectors must be 3D vectors..."; 739 } 740 $DotProduct = $This->GetX() * $Other->GetX + $This->GetY() * $Other->GetY() + $This->GetZ * $Other->GetZ(); 741 return $DotProduct; 742 } 743 else { 744 # Do a string concatenation and return the string... 745 if ($OrderFlipped) { 746 return $Other . $This->StringifyVector(); 747 } 748 else { 749 return $This->StringifyVector() . $Other; 750 } 751 } 752 } 753 754 # 755 # Vector cross product operator genrates a new vector which is the cross 756 # product of two 3D vectors. 757 # 758 # For two vectors, V1 (X1, Y1, Z1) and V2 (X2, Y2, Z2), cross product 759 # V1 x V2 corresponds: (Y1.Z2 - Z1.Y2), (Z1.X2 - X1.Z2), (X1.Y2 - Y1.X2) 760 # 761 sub _VectorCrossProductOperator { 762 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 763 764 $ErrorMsg = "_VectorCrossProductOperator: Vector cross product failed"; 765 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 766 767 if (!$OtherIsVector) { 768 croak "Error: ${ClassName}->${ErrorMsg}: Both object must be vectors..."; 769 } 770 771 # Calculate cross product of two 3D vectors... 772 if ($This->GetSize() != 3) { 773 croak "Error: ${ClassName}->${ErrorMsg}: Both vectors must be 3D vectors..."; 774 } 775 my($Vector, $X, $Y, $Z); 776 $X = $This->GetY() * $Other->GetZ() - $This->GetZ() * $Other->GetY(); 777 $Y = $This->GetZ() * $Other->GetX() - $This->GetX() * $Other->GetZ(); 778 $Z = $This->GetX() * $Other->GetY() - $This->GetY() * $Other->GetX(); 779 780 $Vector = (ref $This)->new($X, $Y, $Z); 781 782 return $Vector; 783 } 784 785 # 786 # Vector booelan operator checks whether a vector contains at least one non-zero 787 # value... 788 # 789 sub _VectorBooleanOperator { 790 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 791 792 $ErrorMsg = "_VectorBooleanOperator: Vector boolean operation failed"; 793 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 794 795 my($Size, $Index); 796 $Size = $This->GetSize(); 797 798 for $Index (0 .. ($Size - 1)) { 799 if ($This->{Values}[$Index] != 0) { 800 return 1; 801 } 802 } 803 return 0; 804 } 805 806 # 807 # Vector not booelan operator checks whether all values of a vector are zero. 808 # 809 sub _VectorNotBooleanOperator { 810 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 811 812 $ErrorMsg = "_VectorNotBooleanOperator: Vector not boolean operation failed"; 813 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 814 815 my($Size, $Index); 816 $Size = $This->GetSize(); 817 818 for $Index (0 .. ($Size - 1)) { 819 if ($This->{Values}[$Index] != 0) { 820 return 0; 821 } 822 } 823 return 1; 824 } 825 826 # 827 # Vector equal operator supports two modes: 828 # . Comparion of corresponding values in two vectors 829 # . Comparing vectors values to a scalar ($Vector == 2) 830 # 831 # Caveats: 832 # . Comparison of a scalar to vector values is not allowed (2 == $Vector) 833 # 834 sub _VectorEqualOperator { 835 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg, $CheckVectorSizes); 836 837 $ErrorMsg = "_VectorEqualOperator: Vector equal comparison failed"; 838 $CheckVectorSizes = 0; 839 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckVectorSizes); 840 841 # Do the comparison... 842 my($ThisSize, $Index); 843 $ThisSize = $This->GetSize(); 844 845 if ($OtherIsVector) { 846 # $OrderFlipped is set to false for two vectors... 847 my($OtherSize) = $Other->GetSize(); 848 if ($ThisSize != $OtherSize) { 849 return 0; 850 } 851 for $Index (0 .. ($ThisSize -1)) { 852 if ($This->{Values}[$Index] != $Other->{Values}[$Index]) { 853 return 0; 854 } 855 } 856 } 857 else { 858 if ($OrderFlipped) { 859 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 860 } 861 # Scalar comparison... 862 for $Index (0 .. ($ThisSize -1)) { 863 if ($This->{Values}[$Index] != $Other) { 864 return 0; 865 } 866 } 867 } 868 return 1; 869 } 870 871 # 872 # Vector not equal operator supports two modes: 873 # . Comparion of corresponding values in two vectors 874 # . Comparing vectors values to a scalar ($Vector != 2) 875 # 876 # Caveats: 877 # . Comparison of a scalar to vector values is not allowed (2 != $Vector2) 878 # 879 sub _VectorNotEqualOperator { 880 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg, $CheckVectorSizes); 881 882 $ErrorMsg = "_VectorNotEqualOperator: Vector not equal comparison failed"; 883 $CheckVectorSizes = 0; 884 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_, $CheckVectorSizes); 885 886 # Do the comparison... 887 my($ThisSize, $Index); 888 $ThisSize = $This->GetSize(); 889 890 if ($OtherIsVector) { 891 # $OrderFlipped is set to false for two vectors... 892 my($OtherSize) = $Other->GetSize(); 893 if ($ThisSize != $OtherSize) { 894 return 1; 895 } 896 for $Index (0 .. ($ThisSize -1)) { 897 if ($This->{Values}[$Index] == $Other->{Values}[$Index]) { 898 return 0; 899 } 900 } 901 } 902 else { 903 if ($OrderFlipped) { 904 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 905 } 906 # Scalar comparison... 907 for $Index (0 .. ($ThisSize -1)) { 908 if ($This->{Values}[$Index] == $Other) { 909 return 0; 910 } 911 } 912 } 913 return 1; 914 } 915 916 # 917 # Vector less than operator supports two modes: 918 # . Comparion of corresponding values in two vectors 919 # . Comparing vectors values to a scalar ($Vector < 2) 920 # 921 # Caveats: 922 # . Comparison of a scalar to vector values is not allowed (2 < $Vector2) 923 # 924 sub _VectorLessThanOperator { 925 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 926 927 $ErrorMsg = "_VectorLessThanOperator: Vector less than comparison failed"; 928 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 929 930 # Do the comparison... 931 my($ThisSize, $Index); 932 $ThisSize = $This->GetSize(); 933 934 if ($OtherIsVector) { 935 # $OrderFlipped is set to false for two vectors... 936 for $Index (0 .. ($ThisSize -1)) { 937 if ($This->{Values}[$Index] >= $Other->{Values}[$Index]) { 938 return 0; 939 } 940 } 941 } 942 else { 943 if ($OrderFlipped) { 944 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 945 } 946 # Scalar comparison... 947 for $Index (0 .. ($ThisSize -1)) { 948 if ($This->{Values}[$Index] >= $Other) { 949 return 0; 950 } 951 } 952 } 953 return 1; 954 } 955 956 # 957 # Vector less than equla operator supports two modes: 958 # . Comparion of corresponding values in two vectors 959 # . Comparing vectors values to a scalar ($Vector <= 2) 960 # 961 # Caveats: 962 # . Comparison of a scalar to vector values is not allowed (2 <= $Vector2) 963 # 964 sub _VectorLessThanEqualOperator { 965 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 966 967 $ErrorMsg = "_VectorLessThanEqualOperator: Vector less than equal comparison failed"; 968 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 969 970 # Do the comparison... 971 my($ThisSize, $Index); 972 $ThisSize = $This->GetSize(); 973 974 if ($OtherIsVector) { 975 # $OrderFlipped is set to false for two vectors... 976 for $Index (0 .. ($ThisSize -1)) { 977 if ($This->{Values}[$Index] > $Other->{Values}[$Index]) { 978 return 0; 979 } 980 } 981 } 982 else { 983 if ($OrderFlipped) { 984 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 985 } 986 # Scalar comparison... 987 for $Index (0 .. ($ThisSize -1)) { 988 if ($This->{Values}[$Index] > $Other) { 989 return 0; 990 } 991 } 992 } 993 return 1; 994 } 995 996 # 997 # Vector greatar than operator supports two modes: 998 # . Comparion of corresponding values in two vectors 999 # . Comparing vectors values to a scalar ($Vector > 2) 1000 # 1001 # Caveats: 1002 # . Comparison of a scalar to vector values is not allowed (2 > $Vector2) 1003 # 1004 sub _VectorGreatarThanOperator { 1005 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1006 1007 $ErrorMsg = "_VectorGreatarThanOperator: Vector greatar than comparison failed"; 1008 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1009 1010 # Do the comparison... 1011 my($ThisSize, $Index); 1012 $ThisSize = $This->GetSize(); 1013 1014 if ($OtherIsVector) { 1015 # $OrderFlipped is set to false for two vectors... 1016 for $Index (0 .. ($ThisSize -1)) { 1017 if ($This->{Values}[$Index] <= $Other->{Values}[$Index]) { 1018 return 0; 1019 } 1020 } 1021 } 1022 else { 1023 if ($OrderFlipped) { 1024 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 1025 } 1026 # Scalar comparison... 1027 for $Index (0 .. ($ThisSize -1)) { 1028 if ($This->{Values}[$Index] <= $Other) { 1029 return 0; 1030 } 1031 } 1032 } 1033 return 1; 1034 } 1035 1036 # 1037 # Vector greatar than equal operator supports two modes: 1038 # . Comparion of corresponding values in two vectors 1039 # . Comparing vectors values to a scalar ($Vector >= 2) 1040 # 1041 # Caveats: 1042 # . Comparison of a scalar to vector values is not allowed (2 <= $Vector2) 1043 # 1044 sub _VectorGreatarThanEqualOperator { 1045 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1046 1047 $ErrorMsg = "_VectorGreatarThanEqualOperator: Vector greatar than equal comparison failed"; 1048 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1049 1050 # Do the comparison... 1051 my($ThisSize, $Index); 1052 $ThisSize = $This->GetSize(); 1053 1054 if ($OtherIsVector) { 1055 # $OrderFlipped is set to false for two vectors... 1056 for $Index (0 .. ($ThisSize -1)) { 1057 if ($This->{Values}[$Index] < $Other->{Values}[$Index]) { 1058 return 0; 1059 } 1060 } 1061 } 1062 else { 1063 if ($OrderFlipped) { 1064 croak "Error: ${ClassName}->${ErrorMsg}: First object must be a vector..."; 1065 } 1066 # Scalar comparison... 1067 for $Index (0 .. ($ThisSize -1)) { 1068 if ($This->{Values}[$Index] < $Other) { 1069 return 0; 1070 } 1071 } 1072 } 1073 return 1; 1074 } 1075 1076 # 1077 # Vector negative value operator returns a vector with values corresponding to 1078 # negative values of a vector 1079 # 1080 sub _VectorNegativeValueOperator { 1081 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1082 1083 $ErrorMsg = "_VectorNegativeValueOperator: Vector negative value operation failed"; 1084 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1085 1086 # Take the negative value... 1087 my($Vector, $ThisSize, $Index); 1088 $Vector = $This->Copy(); 1089 $ThisSize = $This->GetSize(); 1090 1091 for $Index (0 .. ($ThisSize -1)) { 1092 $Vector->{Values}[$Index] = - $This->{Values}[$Index]; 1093 } 1094 return $Vector; 1095 } 1096 1097 # 1098 # Vector absolute value operator returns a vector with values corresponding to 1099 # absolute values of a vector 1100 # 1101 sub _VectorAbsoluteValueOperator { 1102 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1103 1104 $ErrorMsg = "_VectorAbsoluteValueOperator: Vector absolute value operation failed"; 1105 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1106 1107 # Take the absolute value... 1108 my($Vector, $ThisSize, $Index); 1109 $Vector = $This->Copy(); 1110 $ThisSize = $This->GetSize(); 1111 1112 for $Index (0 .. ($ThisSize -1)) { 1113 $Vector->{Values}[$Index] = abs $This->{Values}[$Index]; 1114 } 1115 return $Vector; 1116 } 1117 1118 # 1119 # Vector exp natural base operator returns a vector with values corresponding to 1120 # e raised to the power of values in a vector 1121 # 1122 sub _VectorExpNaturalBaseOperator { 1123 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1124 1125 $ErrorMsg = "_VectorExpNaturalBaseOperator: Vector exp operation failed"; 1126 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1127 1128 # Take the absolute value... 1129 my($Vector, $ThisSize, $Index); 1130 $Vector = $This->Copy(); 1131 $ThisSize = $This->GetSize(); 1132 1133 for $Index (0 .. ($ThisSize -1)) { 1134 $Vector->{Values}[$Index] = exp $This->{Values}[$Index]; 1135 } 1136 return $Vector; 1137 } 1138 1139 # 1140 # Vector log natural base operator returns a vector with values corresponding to 1141 # log of values in a vector 1142 # 1143 sub _VectorLogNaturalBaseOperator { 1144 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1145 1146 $ErrorMsg = "_VectorLogNaturalBaseOperator: Vector log operation failed"; 1147 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1148 1149 # Take the absolute value... 1150 my($Vector, $ThisSize, $Index); 1151 $Vector = $This->Copy(); 1152 $ThisSize = $This->GetSize(); 1153 1154 for $Index (0 .. ($ThisSize -1)) { 1155 $Vector->{Values}[$Index] = log $This->{Values}[$Index]; 1156 } 1157 return $Vector; 1158 } 1159 1160 # 1161 # Vector cosine operator returns a vector with values corresponding to cosine of values 1162 # in a vector. Input vector values are assumed to be in radians. 1163 # 1164 sub _VectorCosineOperator { 1165 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1166 1167 $ErrorMsg = "_VectorCosineOperator: Vector cos operation failed"; 1168 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1169 1170 # Take the absolute value... 1171 my($Vector, $ThisSize, $Index); 1172 $Vector = $This->Copy(); 1173 $ThisSize = $This->GetSize(); 1174 1175 for $Index (0 .. ($ThisSize -1)) { 1176 $Vector->{Values}[$Index] = cos $This->{Values}[$Index]; 1177 } 1178 return $Vector; 1179 } 1180 1181 # 1182 # Vector sine operator returns a vector with values corresponding to sine of values 1183 # in a vector. Input vector values are assumed to be in radians. 1184 # 1185 sub _VectorSineOperator { 1186 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1187 1188 $ErrorMsg = "_VectorSineOperator: Vector sin operation failed"; 1189 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1190 1191 # Take the absolute value... 1192 my($Vector, $ThisSize, $Index); 1193 $Vector = $This->Copy(); 1194 $ThisSize = $This->GetSize(); 1195 1196 for $Index (0 .. ($ThisSize -1)) { 1197 $Vector->{Values}[$Index] = sin $This->{Values}[$Index]; 1198 } 1199 return $Vector; 1200 } 1201 1202 # 1203 # Vector square root returns a vector with values corresponding to sqrt of values 1204 # in a vector. 1205 # 1206 sub _VectorSquareRootOperator { 1207 my($This, $Other, $OrderFlipped, $OtherIsVector, $ErrorMsg); 1208 1209 $ErrorMsg = "_VectorSquareRootOperator: Vector sqrt operation failed"; 1210 ($This, $Other, $OrderFlipped, $OtherIsVector) = _ProcessOverloadedOperatorParameters($ErrorMsg, @_); 1211 1212 # Take the absolute value... 1213 my($Vector, $ThisSize, $Index); 1214 $Vector = $This->Copy(); 1215 $ThisSize = $This->GetSize(); 1216 1217 for $Index (0 .. ($ThisSize -1)) { 1218 $Vector->{Values}[$Index] = sqrt $This->{Values}[$Index]; 1219 } 1220 return $Vector; 1221 } 1222 1223 # Turn vector into array for @{$Vector} operation... 1224 sub _VectorToArrayOperator { 1225 my($This) = @_; 1226 1227 return \@{$This->{Values}}; 1228 } 1229 1230 # Turn vector into number for $#Vector operation: It's the size of vector... 1231 sub _NumifyVector { 1232 my($This) = @_; 1233 1234 return $This->GetSize(); 1235 } 1236 1237 # Process parameters passed to overloaded operators... 1238 # 1239 # For uninary operators, $SecondParameter is not defined. 1240 sub _ProcessOverloadedOperatorParameters { 1241 my($ErrorMsg, $FirstParameter, $SecondParameter, $ParametersOrderStatus, $CheckVectorSizesStatus) = @_; 1242 my($This, $Other, $OrderFlipped, $OtherIsVector, $CheckVectorSizes); 1243 1244 ($This, $Other) = ($FirstParameter, $SecondParameter); 1245 $OrderFlipped = (defined($ParametersOrderStatus) && $ParametersOrderStatus) ? 1 : 0; 1246 $CheckVectorSizes = (defined $CheckVectorSizesStatus) ? $CheckVectorSizesStatus : 1; 1247 1248 _ValidateVector($ErrorMsg, $This); 1249 1250 $OtherIsVector = 0; 1251 if (defined($Other) && (ref $Other)) { 1252 # Make sure $Other is a vector... 1253 _ValidateVector($ErrorMsg, $Other); 1254 if ($CheckVectorSizes) { 1255 _ValidateVectorSizesAreEqual($ErrorMsg, $This, $Other); 1256 } 1257 $OtherIsVector = 1; 1258 } 1259 return ($This, $Other, $OrderFlipped, $OtherIsVector); 1260 } 1261 1262 # Is it a vector object? 1263 sub _IsVector { 1264 my($Object) = @_; 1265 1266 return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0; 1267 } 1268 1269 # Make sure it's a vector reference... 1270 sub _ValidateVector { 1271 my($ErrorMsg, $Vector) = @_; 1272 1273 if (!_IsVector($Vector)) { 1274 croak "Error: ${ClassName}->${ErrorMsg}: Object must be a vector..."; 1275 } 1276 } 1277 1278 # Make sure size of the two vectors contain the same number of values... 1279 sub _ValidateVectorSizesAreEqual { 1280 my($ErrorMsg, $Vector1, $Vector2) = @_; 1281 1282 if ($Vector1->GetSize() != $Vector2->GetSize()) { 1283 croak "Error: ${ClassName}->${ErrorMsg}: Size of the vectors must be same..."; 1284 } 1285 } 1286 1287 # Return a string containing vector values... 1288 sub StringifyVector { 1289 my($This) = @_; 1290 my($VectorString, $FormatString, $PrintFormat, $Size, @ValuesFormat); 1291 1292 $PrintFormat = (exists $This->{ValueFormat}) ? $This->{ValueFormat} : $ValueFormat; 1293 1294 @ValuesFormat = ($PrintFormat) x scalar @{$This->{Values}}; 1295 $FormatString = join ' ', @ValuesFormat; 1296 1297 $Size = $This->GetSize(); 1298 1299 $VectorString = sprintf "<Size: $Size; Values: [ $FormatString ] >", @{$This->{Values}}; 1300 1301 return $VectorString; 1302 } 1303