package Algorithm::FuzzyCmeans::Distance::Cosine;

use strict;
use warnings;
use base qw(Algorithm::FuzzyCmeans::Distance);

sub _norm {
    my ($self, $v) = @_;
    return 0 if !$v;
    my $result = 0;
    map { $result += $_ * $_ } values %{ $v };
    return sqrt($result);
}

sub _inner_product {
    my ($self, $v1, $v2) = @_;
    return 0 if !$v1 || !$v2;

    my @keys = scalar(keys %{ $v1 }) < scalar(keys %{ $v2 }) ?
        keys %{ $v1 } : keys %{ $v2 };
    my $prod = 0;
    foreach my $key (@keys) {
        $prod += $v1->{$key} * $v2->{$key} if $v1->{$key} && $v2->{$key};
    }
    return $prod;
}

sub distance {
    my ($self, $vec1, $vec2) = @_;
    my $nrm1 = $self->_norm($vec1);
    my $nrm2 = $self->_norm($vec2);
    my $cos = $nrm1 && $nrm2 ?
        $self->_inner_product($vec1, $vec2) / ($nrm1 * $nrm2) : 0;
    return 1 - $cos;
}

1;

__END__

=head1 NAME

Algorithm::FuzzyCmeans::Distance::Cosine

=head1 DESCRIPTION

Calculate the cosine distance between input vectors.

=head1 AUTHOR

Mizuki Fujisawa E<lt>fujisawa@bayon.ccE<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut