package Math::Vector::Real::Random;

our $VERSION = '0.02';

package Math::Vector::Real;

use strict;
use warnings;
use Carp;

use Math::Random ();

use constant _PI => 3.14159265358979323846264338327950288419716939937510;

sub random_in_box {
    if (ref $_[0]) {
        my $box = shift;
        return bless [map Math::Random::random_uniform(1, 0, $_), @$box], ref $box;
    }
    else {
        my ($class, $dim, $size) = @_;
        $size = 1 unless defined $size;
        return bless [Math::Random::random_uniform($dim, 0, $size)], $class;
    }
}

sub random_in_sphere {
    my ($class, $dim, $size) = @_;
    $size ||= 1;
    my $n = $class->random_versor($dim);
    my $f = Math::Random::random_uniform(1, 0, 1) ** (1/$dim);
    $_ *= $f for @$n;
    $n;
}

sub random_versor {
    my ($class, $dim, $scale) = @_;
    my @n;
    $scale = 1 unless defined $scale;
    if ($dim >= 3) {
        @n = Math::Random::random_normal $dim, 0, 1;
        my $d = 0;
        $d += $_ * $_ for @n;
        unless ($d) {
            $n[0] = $scale;
        }
        else {
            $d = $scale/sqrt($d);
            $_ *= $d for @n;
        }
    }
    elsif ($dim >= 2) {
        my $ang = Math::Random::random_uniform(1, -(_PI), _PI);
        @n = ($scale * sin $ang, $scale * cos $ang);
    }
    elsif ($dim >= 1) {
        @n = (rand >= 0.5 ? $scale : -$scale);
    }
    bless \@n, $class;
}

sub random_normal {
    my ($class, $dim, $sd) = @_;
    $sd ||= 1;
    bless [Math::Random::random_normal $dim, 0, $sd], $class;
}

1;

=head1 NAME

Math::Vector::Real::Random - Generate random real vectors

=head1 SYNOPSIS

  use Math::Vector::Real qw(V);
  use Math::Vector::Real::Random;

  my $v = Math::Vector::Real->random_normal(7);
  my $v2 = $c->random_normal;

=head1 DESCRIPTION

This module extends the L<Math::Vector::Real> package adding some
methods for random generation of vectors.

=head2 Methods

The extra methods are:

=over 4

=item Math::Vector::Real->random_in_box($dim, $size)

=item $v->random_in_box

When called as a class method, returns a random vector of dimension
C<$dim> contained inside the hypercube of the given size.

When called as an instance method, returns a random vector contained
in the box defined by the given vector instance.

=item Math::Vector::Real->random_in_sphere($dim, $radio)

Returns random vector inside the hyper-sphere of the given dimension
and radio.

=item Math::Vector::Real->random_versor($dim)

Returns a randon vector of norm 1.0.

=item Math::Vector::Real->random_normal($dim, $sd)

Returns a random vector in the space of the given dimension, where
each component follows a normal distribution with standard deviation
C<$sd> (defaults to 1.0).

=back

=head1 SEE ALSO

L<Math::Vector::Real>, L<Math::Random>.

=head1 AUTHORS

Salvador Fandino, E<lt>salva@E<gt>, David Serrano.

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2011 by Salvador Fandino

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.12.3 or,
at your option, any later version of Perl 5 you may have available.


=cut
