package Camila::Set;

use 5.006;
use strict;
use warnings;

use Camila;
use Camila::Boolean;
use Camila::Number;
use Data::Dumper;

require Exporter;

our @ISA = qw(Exporter);

# This allows declaration	use Camila::Set ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw() ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = '0.01';

sub nill   { return bless( +{} ) }
sub new    { return nill }
sub from_values {
  my %self;
  for (@_) {
    $self{$_->repr} = 1;
  }
  return bless(\%self);
}
sub show   {
  my $self = shift;
  return "{ ".join(" ",map {my $x = Camila::regen($_);
			    $x->show} sort keys %{$self})." }";
}
sub repr { return Dumper(shift) }

sub union {
  my ($a,$b) = @_;
  my %left = %$a;
  my %right = %$b;
  my %ME = ();
  @ME{keys  %left}++ if (keys %left);
  @ME{keys %right}++ if (keys %right);;
  return bless(\%ME);
}

sub intersection {
  my ($a,$b) = @_;
  my %left = %$a;
  my %right = %$b;
  my %me = ();
  for (keys %left) {
    $me{$_}++ if exists($right{$_});
  }
  return bless(\%me);
}

sub difference {
  my ($a,$b) = @_;
  my %left = %$a;
  my %right = %$b;
  my %me = ();
  for (keys %left) {
    $me{$_}++ unless exists($right{$_});
  }
  return bless(\%me);
}

sub equal {
  my ($a,$b) = @_;
  my @a = map {Camila::regen($_)} sort keys %$a;
  my @b = map {Camila::regen($_)} sort keys %$b;
  while(@a && @b) {
    $a = shift(@a); $b = shift(@b);
    return Camila::Boolean::false() unless $a->equal($b);
  }
  return Camila::Boolean::false() if @a or @b;
  return Camila::Boolean::true();
}

sub isempty {
  my ($set) = @_;
  if (keys %$set) {
    return Camila::Boolean::false
  } else {
    return Camila::Boolean::true
  }
}

sub member {
  my ($set, $element) = @_;
  my $string = $element->repr;
  for (keys %$set) {
    return Camila::Boolean::true if ($_ eq $string);
  }
  return Camila::Boolean::false;
}

sub inseg {
  my $int = shift;
  $int = $int->value;
  if ($int) {
    my $inc = ($int<0)?-1:1;
    $int = abs($int);
    my @values = ();
    for(my $i=1; $i<=$int; $i++) {
      push @values, Camila::Number::new($inc*$i);
    }
    return from_values(@values);
  } else {
    return Camila::Set::nill();
  }
}

sub choice {
  my $set = shift;
  my @els = keys %$set;
  if (@els) {
    Camila::regen($els[int(rand($#els + 1))]);
  } else {
    # TODO: make an error... no elements to choose from
    return undef;
  }
}

sub card {
  my $set = shift;
  my @els = keys %$set;
  return Camila::Number::new($#els + 1)
}

sub subset {
  my ($r,$s) = @_;
  return $r->equal($r->intersection($s));
}

sub the {
  my $set = shift;
  my @els = keys %$set;
  if (@els == 1) {
    Camila::regen($els[0]);
  } else {
    # TODO: make an error... there are too many, or too few elements
    return undef;
  }
}

sub UNION {
  my $set = shift;
  my @sets = map {Camila::regen($_)} keys %$set;
  if (@sets) {
    $set = shift @sets;
    while(@sets) {
      $set = $set->union(shift @sets)
    }
    return $set
  } else {
    Camila::Set::nill();
  }
}

sub reduce {
  my ($set, $neuter, $operator) = @_;

  # Operator is a string, so the func name is:
  my $operator_function_name = $operator->show;

  my $result = $neuter;
  my @values = keys %$set;
  while(@values) {
    my $current = Camila::regen(shift @values);
    $result = Camila::op($operator_function_name,$result, $current);
  }
  return $result;
}

sub compreension {
  my @info = @_;
  print STDERR Dumper(\@info);
  ""
}

1;
__END__

=head1 NAME

Camila::Set - Perl module extension for managing sets for Camila

=head1 SYNOPSIS

  use Camila::Set;

=head1 DESCRIPTION



=head1 AUTHOR

Alberto Simões, E<lt>albie@alfarrabio.di.uminho.ptE<gt>

=head1 SEE ALSO

L<perl>.

=cut
