package AI::Categorizer::Util;

use Exporter;
use base qw(Exporter);
@EXPORT_OK = qw(intersection average max min random_elements binary_search);

use strict;

# It's possible that this can be a class - something like 
# 
# $e = Evaluate->new(); $e->correct([...]); $e->assigned([...]); print $e->precision;

# A simple binary search
sub binary_search {
  my ($arr, $target) = @_;
  my ($low, $high) = (0, scalar @$arr);
  use integer;
  while ( $low < $high ) {
    my $cur = ($low + $high)/2;
    if ( $arr->[$cur] < $target ) {
      $low = $cur + 1;
    } else {
      $high = $cur;
    }
  }
  return $low;
}

sub max {
  return undef unless @_;
  my $max = shift;
  foreach (@_) {
    $max = $_ if $_ > $max;
  }
  return $max;
}

sub min {
  return undef unless @_;
  my $min = shift;
  foreach (@_) {
    $min = $_ if $_ > $min;
  }
  return $min;
}

sub average {
  return undef unless @_;
  my $total;
  $total += $_ foreach @_;
  return $total/@_;
}

sub intersection {
  my ($one, $two) = @_;
  $two = _hashify($two);

  return UNIVERSAL::isa($one, 'HASH') ?	# Accept hash or array for $one
    grep {exists $two->{$_}} keys %$one :
    grep {exists $two->{$_}} @$one;
}

sub _hashify {
  return $_[0] if UNIVERSAL::isa($_[0], 'HASH');
  return {map {$_=>1} @{$_[0]}};
}

sub random_elements {
  my ($a_ref, $n) = @_;
  return @$a_ref if $n >= @$a_ref;
  
  my ($select, $mode) = ($n < @$a_ref/2) ? ($n, 'include') : (@$a_ref - $n, 'exclude');

  my %i;
  $i{int rand @$a_ref} = 1 while keys(%i) < $select;

  return @{$a_ref}[keys %i] if $mode eq 'include';
  return map {$i{$_} ? () : $a_ref->[$_]} 0..$#$a_ref;
}

1;