2 # This file is part of Language::Befunge.
3 # Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the same terms as Perl itself.
10 package Language
::Befunge
::Vector
;
22 '+=' => \
&_add_inplace
,
23 '-=' => \
&_substract_inplace
,
27 # try to load speed-up LBV
28 eval 'use Language::Befunge::Vector::XS';
29 if ( defined $Language::Befunge
::Vector
::XS
::VERSION
) {
30 my $xsversion = $Language::Befunge
::Vector
::XS
::VERSION
;
33 as_string get_dims get_component get_all_components
36 _add _substract _invert
37 _add_inplace _substract_inplace
40 foreach my $sub ( @subs ) {
42 no warnings
'redefine';
43 my $lbvxs_sub = "Language::Befunge::Vector::XS::$sub";
46 # LBV::XS 1.1.0 adds rasterize()
47 @subs = qw
[ rasterize _xs_rasterize_ptr
];
48 if($xsversion gt "1.0.0") {
49 # import the XS functions from LBVXS
51 no warnings
'redefine';
52 foreach my $sub (@subs) {
53 my $lbvxs_sub = "Language::Befunge::Vector::XS::$sub";
57 # export the pure-perl functions to LBVXS
59 no warnings
'redefine';
60 foreach my $sub (@subs) {
61 my $lbvxs_sub = "Language::Befunge::Vector::XS::$sub";
71 # my $vec = LB::Vector->new( $x [, $y, ...] );
73 # Create a new vector. The arguments are the actual vector data; one
74 # integer per dimension.
80 my $usage = "Usage: $pkg->new(\$x, ...)";
81 croak
$usage unless scalar(@_) > 0;
91 # my $vec = LB::Vector->new_zeroes($dims);
93 # Create a new vector of dimension $dims, set to the origin (all
94 # zeroes). LBV->new_zeroes(2) is exactly equivalent to LBV->new(0, 0).
97 my ($pkg, $dims) = @_;
100 my $usage = "Usage: $pkg->new_zeroes(\$dimensions)";
101 croak
$usage unless defined $dims;
102 croak
$usage unless $dims > 0;
105 my $self = [ (0) x
$dims ];
112 # my $vec = $v->copy;
114 # Return a new LBV object, which has the same dimensions and coordinates
119 return bless [@
$vec], ref $vec;
129 # my $str = $vec->as_string;
132 # Return the stringified form of $vec. For instance, a Befunge vector
133 # might look like "(1,2)".
137 return "(" . join(",",@
$self) . ")";
142 # my $dims = $vec->get_dims;
144 # Return the number of dimensions, an integer.
148 return scalar(@
$self);
153 # my $val = $vec->get_component($dim);
155 # Get the value for dimension $dim.
158 my ($self, $dim) = @_;
159 croak
"No such dimension $dim!" unless $dim >= 0 && $self->get_dims > $dim;
160 return $self->[$dim];
165 # my @vals = $vec->get_all_components;
167 # Get the values for all dimensions, in order from 0..N.
169 sub get_all_components
{
180 # Set the vector back to the origin, all 0's.
184 @
$self = (0) x
$self->get_dims;
189 # $vec->set_component($dim, $value);
191 # Set the value for dimension $dim to $value.
194 my ($self, $dim, $val) = @_;
195 croak
"No such dimension $dim!" unless $dim >= 0 && $self->get_dims > $dim;
196 $self->[$dim] = $val;
203 # my $is_within = $vec->bounds_check($begin, $end);
205 # Check whether $vec is within the box defined by $begin and $end.
206 # Return 1 if vector is contained within the box, and 0 otherwise.
209 my ($vchk, $begin, $end) = @_;
210 croak
"uneven dimensions in bounds check!" unless $vchk->get_dims == $begin->get_dims;
211 croak
"uneven dimensions in bounds check!" unless $vchk->get_dims == $end->get_dims;
212 for (my $d = 0; $d < $vchk->get_dims; $d++) {
213 return 0 if $vchk->get_component($d) < $begin->get_component($d);
214 return 0 if $vchk->get_component($d) > $end->get_component($d);
221 # $vec = $vec->rasterize($min, $max);
223 # Return the next vector in raster order, or undef if the hypercube space
224 # has been fully covered. To enumerate the entire storage area, the caller
225 # should call rasterize on the storage area's "min" value the first time,
226 # and keep looping while the return value is defined. To enumerate a
227 # smaller rectangle, the caller should pass in the min and max vectors
228 # describing the rectangle, and keep looping while the return value is
233 my ($v, $min, $max) = @_;
234 return undef unless $v->bounds_check($min, $max);
236 my $nd = $v->get_dims();
237 for my $d (0..$nd-1) {
238 if($v->get_component($d) >= $max->get_component($d)) {
239 # wrap to the next highest dimension, continue loop
240 $v->set_component($d, $min->get_component($d));
242 # still have farther to go in this dimension.
243 $v->set_component($d, $v->get_component($d) + 1);
247 # ran out of dimensions!
258 # my $vec = $v1->_add($v2);
259 # my $vec = $v1 + $v2;
261 # Return a new LBV object, which is the result of $v1 plus $v2.
265 my $nd = scalar @
$v1;
266 croak
"uneven dimensions in vector addition!" unless $nd == scalar @
$v2;
267 return ref($v1)->new(map { $$v1[$_] + $$v2[$_] } (0..$nd-1));
272 # my $vec = $v1->_substract($v2);
273 # my $vec = $v1 - $v2;
275 # Return a new LBV object, which is the result of $v1 minus $v2.
279 my $nd = scalar @
$v1;
280 croak
"uneven dimensions in vector subtraction!" unless $nd == scalar @
$v2;
281 return ref($v1)->new(map { $$v1[$_] - $$v2[$_] } (0..$nd-1));
286 # my $v2 = $v1->_invert;
289 # Subtract $v1 from the origin. Effectively, gives the inverse of the
290 # original vector. The new vector is the same distance from the origin,
291 # in the opposite direction.
295 my $nd = scalar @
$v1;
296 return ref($v1)->new(map { -$_ } (@
$v1));
303 # $v1->_add_inplace($v2);
309 my $nd = scalar @
$v1;
310 croak
"uneven dimensions in vector addition!" unless $nd == scalar @
$v2;
311 map { $$v1[$_] += $$v2[$_] } (0..$nd-1);
317 # $v1->_substract_inplace($v2);
320 # Substract $v2 to $v1, and stores the result back into $v1.
322 sub _substract_inplace
{
324 my $nd = scalar @
$v1;
325 croak
"uneven dimensions in vector substraction!" unless $nd == scalar @
$v2;
326 map { $$v1[$_] -= $$v2[$_] } (0..$nd-1);
334 # my $bool = $v1->_compare($v2);
335 # my $bool = $v1 <=> $v2;
337 # Check whether the vectors both point at the same spot. Return 0 if they
338 # do, 1 if they don't.
342 my $nd = scalar @
$v1;
343 croak
"uneven dimensions in bounds check!" unless $nd == scalar @
$v2;
344 for (my $d = 0; $d < $nd; $d++) {
345 return 1 if $$v1[$d] != $$v2[$d];
351 #- other private methods
354 # my $ptr = $v1->_xs_rasterize_ptr();
356 # Get a pointer to the C "rasterize" function. Returns undef if LBVXS is not
357 # loaded. This is useful for external XS modules, because calling the C
358 # function directly is faster.
360 # The prototype of the C rasterize function is:
362 # AV *rasterize(AV *vec_array, AV *min_array, AV *max_array);
364 # It operates just like the perl rasterize function, and returns NULL when the
365 # end of the loop has been reached.
367 sub _xs_rasterize_ptr
{ return undef }
375 Language::Befunge::Vector - an opaque, N-dimensional vector class.
381 my $v1 = Language::Befunge::Vector->new($x, $y, ...);
382 my $v2 = Language::Befunge::Vector->new_zeroes($dims);
388 This class abstracts normal vector manipulation. It lets you pass
389 around one argument to your functions, rather than N arguments, one
390 per dimension. This means much of your code doesn't have to care
391 how many dimensions you're working with.
393 You can do vector arithmetic, test for equality, or even stringify
394 the vector to a string like I<"(1,2,3)">.
400 =head2 my $vec = LB::Vector->new( $x [, $y, ...] )
402 Create a new vector. The arguments are the actual vector data; one
403 integer per dimension.
406 =head2 my $vec = LB::Vector->new_zeroes($dims);
408 Create a new vector of dimension C<$dims>, set to the origin (all zeroes). C<<
409 LBV->new_zeroes(2) >> is exactly equivalent to B<< LBV->new(0,0) >>.
412 =head2 my $vec = $v->copy;
414 Return a new LBV object, which has the same dimensions and coordinates
419 =head1 PUBLIC METHODS
421 =head2 my $str = $vec->as_string;
423 Return the stringified form of C<$vec>. For instance, a Befunge vector
424 might look like C<(1,2)>.
426 This method is also applied to stringification, ie when one forces
427 string context (C<"$vec">).
430 =head2 my $dims = $vec->get_dims;
432 Return the number of dimensions, an integer.
435 =head2 my $val = $vec->get_component($dim);
437 Get the value for dimension C<$dim>.
440 =head2 my @vals = $vec->get_all_components;
442 Get the values for all dimensions, in order from 0..N.
447 Set the vector back to the origin, all 0's.
450 =head2 $vec->set_component($dim, $value);
452 Set the value for dimension C<$dim> to C<$value>.
455 =head2 my $is_within = $vec->bounds_check($begin, $end);
457 Check whether C<$vec> is within the box defined by C<$begin> and C<$end>.
458 Return 1 if vector is contained within the box, and 0 otherwise.
461 =head2 $vec->rasterize($min, $max);
463 Return the next vector in raster order, or undef if the hypercube space
464 has been fully covered.
466 To enumerate the entire storage area, the caller should call rasterize
467 on the storage area's "min" value the first time, and keep looping while
468 the return value is defined. To enumerate a smaller rectangle, the
469 caller should pass in the min and max vectors describing the rectangle,
470 and keep looping while the return value is defined.
474 =head1 MATHEMATICAL OPERATIONS
476 =head2 Standard operations
478 One can do some maths on the vectors. Addition and substraction work as
484 Either operation return a new LBV object, which is the result of C<$v1>
487 The inversion is also supported:
490 will subtracts C<$v1> from the origin, and effectively, gives the
491 inverse of the original vector. The new vector is the same distance from
492 the origin, in the opposite direction.
495 =head2 Inplace operations
497 LBV objects also supports inplace mathematical operations:
502 effectively adds / substracts C<$v2> to / from C<$v1>, and stores the
503 result back into C<$v1>.
508 Finally, LBV objects can be tested for equality, ie whether two vectors
509 both point at the same spot.
511 print "same" if $v1 == $v2;
512 print "differ" if $v1 != $v2;
515 =head1 PRIVATE METHODS
517 =head2 _xs_rasterize_ptr
519 my $ptr = $v1->_xs_rasterize_ptr();
521 Get a pointer to the C "rasterize" function. Returns undef if LBVXS is not
522 loaded. This is useful for external XS modules, to allow them to call the
523 C function directly for additional speed.
525 The prototype of the C rasterize function is:
527 AV *rasterize(AV *vec_array, AV *min_array, AV *max_array);
529 It operates just like the perl rasterize function, and returns NULL when the
530 end of the loop has been reached.
540 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
542 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
545 =head1 COPYRIGHT & LICENSE
547 Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
549 This program is free software; you can redistribute it and/or modify
550 it under the same terms as Perl itself.