File Coverage

blib/lib/LinAlg/Vector.pm
Criterion Covered Total %
statement 110 111 99.1
branch 8 8 100.0
condition n/a
subroutine 26 27 96.3
pod 19 19 100.0
total 163 165 98.7


line stmt bran cond sub pod time code
1             ##############################################################################
2             # Package/use statements
3             ##############################################################################
4              
5             package LinAlg::Vector;
6              
7 3     3   164126 use 5.006;
  3         15  
8 3     3   22 use strict;
  3         8  
  3         85  
9 3     3   20 use warnings;
  3         14  
  3         126  
10              
11 3     3   1394 use Math::Round q(:all);
  3         27528  
  3         690  
12 3     3   2076 use Moose;
  3         1767697  
  3         24  
13 3     3   27942 use Params::Check::Item;
  3         7859  
  3         264  
14              
15             BEGIN {
16 3     3   4131 our $VERSION = '0.01';
17             }
18              
19             ##############################################################################
20             # class definition
21             ##############################################################################
22              
23             around BUILDARGS => sub {
24             my $orig = shift;
25             my $class = shift;
26             my @args = @_;
27              
28             if(scalar(@args) == 1 && ref($args[0]) eq "ARRAY") {
29             my @arr = @{$args[0]};
30             my @copy = @arr[0..$#arr];
31             return $class->$orig(data=>\@copy);
32             } else {
33             return $class->$orig(@args);
34             }
35             };
36              
37             has _v => (
38             is => "rw",
39             isa => "ArrayRef[Num]",
40             default => sub { [] },
41             init_arg => "data"
42             );
43              
44             sub raw {
45 4     4 1 2780 my ($self) = @_[0..0];
46 4         18 my @copy = @{$self->_v}[0..$self->len-1];
  4         145  
47 4         30 return \@copy;
48             }
49              
50             sub toString {
51 12     12 1 5362 my ($self) = @_[0..0];
52 12         24 return "[".(join(',', @{$self->_v}))."]";
  12         485  
53             }
54              
55             sub len {
56 534     534 1 3350 my $self = shift;
57 534         763 return scalar(@{$self->_v});
  534         13765  
58             }
59              
60             sub eq {
61 24     24 1 13316 my ($self, $b, $pow) = @_[0..2];
62 24         96 checkClass($b, "LinAlg::Vector", "argument is not LinAlg::Vector");
63 24         496 checkNumEQ($self->len, $b->len, "vectors are different lens");
64              
65 24         371 my $doRound = 0;
66 24         42 my $roundTo = 1;
67 24 100       62 if(defined($pow)) {
68 2         7 checkIndex(abs($pow), "invalid power of 10 to round to");
69 2         28 $doRound = 1;
70 2         21 $roundTo = 10**($pow);
71             }
72              
73 24 100       56 if($doRound) {
74 2         6 for(my $i=0; $i<$self->len; $i++) {
75 3 100       6 if(nearest($roundTo,$self->get($i)) != nearest($roundTo,$b->get($i))) {
76 1         15 return 0;
77             }
78             }
79             } else {
80 22         59 for(my $i=0; $i<$self->len; $i++) {
81 43 100       129 if($self->get($i) != $b->get($i)) {
82 6         49 return 0;
83             }
84             }
85             }
86 17         114 return 1;
87             }
88              
89              
90             sub get {
91 290     290 1 1793 my ($self, $num) = @_[0..1];
92 290         788 checkNumber($num, "not number");
93 290         4335 checkNumLT($num, $self->len, "invalid vector index");
94              
95 289         11313 return $self->_v->[$num];
96             }
97            
98             #NOTE: cannot grow vector after created!
99             sub set {
100 5     5 1 1181 my ($self, $num, $val) = @_[0..2];
101 5         20 checkNumber($num, "not number");
102 5         82 checkNumber($val, "not number");
103 4         65 checkNumLT($num, $self->len, "invalid vector index");
104              
105 3         89 return $self->_v->[$num] = $val;
106             }
107              
108             sub copy {
109 1     1 1 542 my $self = shift;
110 1         4 my @newData = @{$self->_v}[0..$self->len-1];
  1         21  
111 1         21 return LinAlg::Vector->new(data=>\@newData);
112             }
113              
114              
115             sub add {
116 5     5 1 2854 my ($self, $b) = @_[0..1];
117 5         20 checkClass($b, "LinAlg::Vector", "argument is not LinAlg::Vector");
118 4         71 checkNumEQ($self->len, $b->len, "vectors are different lens");
119              
120 3         55 my @newData = ();
121 3         11 foreach my $i (0..$self->len-1) {
122 8         21 push(@newData, $self->get($i)+$b->get($i))
123             }
124 3         61 return LinAlg::Vector->new(data=>\@newData);
125             }
126              
127             sub subt {
128 5     5 1 3466 my ($self, $b) = @_[0..1];
129 5         21 checkClass($b, "LinAlg::Vector", "argument is not LinAlg::Vector");
130 4         78 checkNumEQ($self->len, $b->len, "vectors are different lens");
131              
132 3         55 my @newData = ();
133 3         12 foreach my $i (0..$self->len-1) {
134 8         23 push(@newData, $self->get($i) - $b->get($i))
135             }
136 3         79 return LinAlg::Vector->new(data=>\@newData);
137             }
138              
139             sub dot {
140 23     23 1 2959 my ($self, $b) = @_[0..1];
141 23         73 checkClass($b, "LinAlg::Vector", "argument is not LinAlg::Vector");
142 22         373 checkNumEQ($self->len, $b->len, "vectors are different lens");
143              
144 21         269 my $sum = 0;
145 21         45 foreach my $i (0..$self->len-1) {
146 44         87 $sum += $self->get($i) * $b->get($i);
147             }
148 21         96 return $sum;
149             }
150              
151 13     13 1 649 sub x { my $self = shift; return $self->get(0); }
  13         39  
152 13     13 1 587 sub y { my $self = shift; return $self->get(1); }
  13         38  
153 13     13 1 907 sub z { my $self = shift; return $self->get(2); }
  13         45  
154              
155             sub cross {
156 5     5 1 5195 my ($self, $b) = @_[0..1];
157 5         33 checkClass($b, "LinAlg::Vector", "argument is not LinAlg::Vector");
158 4         122 checkNumEQ($self->len, $b->len, "vectors are different lens");
159 3         71 checkNumEQ($self->len, 3, "can only cross-product 3-dim vectors");
160              
161 3         67 my $x = $self->y*$b->z - $self->z*$b->y;
162 3         14 my $y = $self->z*$b->x - $self->x*$b->z;
163 3         16 my $z = $self->x*$b->y - $self->y*$b->x;
164 3         120 return LinAlg::Vector->new(data=>[$x, $y, $z]);
165             }
166              
167             sub scale {
168 16     16 1 3291 my ($self, $b) = @_[0..1];
169 16         47 checkNumber($b, "argument is not number");
170              
171 15         207 my @newData = ();
172 15         29 foreach my $i (0..$self->len-1) {
173 32         103 push(@newData, $self->get($i)*$b);
174             }
175 15         362 return LinAlg::Vector->new(data=>\@newData);
176             }
177              
178             sub mag {
179 15     15 1 1661 my ($self) = @_[0..0];
180 15         34 return sqrt($self->dot($self));
181             }
182              
183             sub unit {
184 8     8 1 2049 my ($self) = @_[0..0];
185 8         18 return $self->scale(1/$self->mag());
186             }
187              
188             sub proj {
189 4     4 1 2069 my ($self, $b) = @_[0..1];
190 4         15 checkClass($b, "LinAlg::Vector", "argument is not LinAlg::Vector");
191 4         68 checkNumEQ($self->len, $b->len, "vectors are different lens");
192 4         60 return $b->unit()->scale($self->dot($b)/$b->mag());
193             }
194              
195             sub rotate {
196 0     0 1   checkImpl("LinAlg::Vector::rotate");
197             }
198              
199             ##############################################################################
200             # Add interfaces and end class definition
201             ##############################################################################
202              
203 3     3   29 no Moose;
  3         12  
  3         23  
204             __PACKAGE__->meta->make_immutable;
205              
206             1; # End of Params::Check::Item
207              
208             ##############################################################################
209             # Perldoc
210             ##############################################################################
211              
212             =head1 NAME
213              
214             LinAlg::Vector - Extensive vector library based on Moose class system.
215              
216             =head1 VERSION
217              
218             Version 0.01
219              
220             =head1 SYNOPSIS
221              
222             LinAlg::Vector proveds an object-oriented interface for creating and using
223             vectors composed of numbers. It supports most mathematical functions such
224             as add, subtract, dot, cross, scale, unit, and projection. Additionally,
225             convenience functions for comparing vectors, and stringifying them are
226             also provided.
227              
228             All vector methods, except for C<set>, will not modify the underlying
229             vector -- they all will return new vectors.
230              
231             An example of performing the triple-product of three vectors:
232              
233             use LinAlg::Vector;
234              
235             my $v1 = LinAlg::Vector->new([1,2,3]);
236             my $v2 = LinAlg::Vector->new([4,5,6]);
237             my $v3 = LinAlg::Vector->new([7,8,9]);
238             my $s1 = $v1->dot($v2->cross($v3));
239              
240             =head1 CONSTRUCTOR
241              
242             The constructor takes in either a hash or an Array reference. The only
243             valid key-val pair in the hash is C<data=>[]>. Examples of using the
244             constructor are:
245            
246             LinAlg::Vector->new(data=>[1,2,3]);
247             LinAlg::Vector->new([1,2,3]);
248              
249             =head1 METHODS
250              
251             =head2 raw
252              
253             returns a copy of the underlying data array. If you manipulate the
254             returned copy, it will not affect the original vector data.
255              
256             =head2 toString
257              
258             returns a stringified version of the vector
259              
260             =head2 len
261              
262             returns the length of the vector
263              
264             =head2 eq VEC2,[PRECISION]
265              
266             returns 1 if this vector is equivalent to VEC2 (same length and
267             element values are all the same). Optinally, you can pass in PRECISION,
268             which can fine-tune what power-of-10 you round elements to when comparing.
269             Examples are:
270              
271             my $v1 = LinAlg::Vector->new([1,2]);
272             my $v2 = LinAlg::Vector->new([1.001,1.999]);
273             $v1->eq($v2); #returns 0
274             $v1->eq($v2, -2); #rounds each element to nearest 10**-2. returns 1
275              
276             =head2 get IDX
277            
278             returns the element at index IDX. Zero-indexed.
279              
280             =head2 set IDX,VAL
281              
282             sets the element VAL at index IDX. Zero-indexed. retuns the value just set.
283              
284             =head2 copy
285              
286             returns copy of LinAlg::Vector, with a copy of the underlying data as well.
287              
288             =head2 add VEC2
289              
290             adds VEC2 to this vector and returns a new vector.
291              
292             =head2 subt VEC2
293              
294             subtracts VEC2 from this vector and returns a new vector.
295              
296             =head2 dot VEC2
297              
298             performs a dot-product with this vector and VEC2 and returns the scalar.
299              
300             =head2 x
301              
302             returns C<get(0)>
303              
304             =head2 y
305              
306             returns C<get(1)>
307              
308             =head2 z
309              
310             returns C<get(2)>
311              
312             =head2 cross VEC2
313              
314             performs cross-product with VEC2 and returns result vector. The operation
315             is 'this' x VEC2. If 'this' and VEC2 are not of length 3, an error is thrown.
316              
317             =head2 scale NUM
318              
319             scales this vector by NUM and returns new vector.
320              
321             =head2 mag
322              
323             returns the magnitude of this vector: sqrt(this->dot(this))
324              
325             =head2 unit
326              
327             returns the unit vector for this vector: this->scale(1/this->mag)
328              
329             =head2 proj VEC2
330              
331             returns the projected vector of this vector onto VEC2.
332              
333             =head2 rotate
334              
335             Not Yet Implemented
336              
337             =head1 SEE ALSO
338              
339             LinAlg::Matrix
340              
341             =head1 AUTHOR
342              
343             Samuel Steffl, C<sam@ssteffl.com>
344              
345             =head1 BUGS
346              
347             Please report any bugs or feature requests through the web interface at
348             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=LinAlg-Vector>.
349             I will be notified, and then you'll automatically be notified of
350             progress on your bug as I make changes.
351              
352             =head1 SUPPORT
353              
354             You can find documentation for this module with the perldoc command.
355              
356             perldoc LinAlg::Matrix
357              
358             =head1 LICENSE AND COPYRIGHT
359              
360             Copyright 2017 Samuel Steffl.
361              
362             This is free software; you can redistribute it and/or modify it under the
363             same terms as the Perl 5 programming language system itself.
364