#!/usr/bin/perl

# setOperation:
# This script performs a set-theory operation on two sets of data.
# (see http://en.wikipedia.org/wiki/Set)
# The first command-line argument should be one of the following strings:
#     A_and_B       intersection of sets A and B
#     A_or_B        union of sets A and B
#     A_and_not_B   relative complement of B in A (elements in A but not in B)
#     B_and_not_A   relative complement of A in B (elements in B but not in A)
# The second and third command-line arguments should be the names of files
# containing the data for the sets A and B respectively.
# These files should have one data-element per line.
# Leading and trailing whitespace on each line will be ignored.
# Empty lines in the files will be ignored.
#
# Cameron Hayne (macdev@hayne.net)  December 2006

use strict;
use warnings;

my $scriptname = "setOperation";
die "Usage: $scriptname operation fileA fileB\n" unless scalar(@ARGV) == 3;

# Function declarations
sub createHash($);
sub printHashKeys($);
sub A_and_B($$);
sub A_or_B($$);
sub A_and_not_B($$);
sub B_and_not_A($$);

my %operations = (
                  'A_and_B'      => \&A_and_B,
                  'A_or_B'       => \&A_or_B,
                  'A_and_not_B'  => \&A_and_not_B,,
                  'B_and_not_A'  => \&B_and_not_A,,
                 );

my $operation = shift @ARGV;
unless ($operations{$operation})
{
    my $availOps = join(", ", sort keys %operations);
    die "Invalid operation ($operation)\nAvailable operations: $availOps\n";
}
my $fileA = shift @ARGV;
die "No such file ($fileA)\n" unless -e $fileA;
my $fileB = shift @ARGV;
die "No such file ($fileB)\n" unless -e $fileB;

my $hashRefA = createHash($fileA);
my $hashRefB = createHash($fileB);
my $sub = $operations{$operation};
my $result = &$sub($hashRefA, $hashRefB);
printHashKeys($result);
exit;

# --------------------
# Functions
# --------------------

sub createHash($)
{
    my ($filepath) = @_;

    my %result = ();
    open(FILE, "<$filepath") or die "Can't open file \"filepath\": $!\n";
    while (<FILE>)
    {
        chomp;
        s/^\s+//; # remove leading whitespace
        s/\s+$//; # remove trailing whitespace
        $result{$_} = 1 unless $_ eq "";
    }
    close(FILE);
    return \%result;
}

sub printHashKeys($)
{
    my ($hashRef) = @_;

    foreach my $key (sort keys %$hashRef)
    {
        print "$key\n";
    }
}

sub A_and_B($$)
{
    my ($hashRefA, $hashRefB) = @_;

    my %result = ();
    foreach my $key (keys %$hashRefA)
    {
        $result{$key} = 1 if exists $hashRefB->{$key};
    }

    return \%result;
}

sub A_or_B($$)
{
    my ($hashRefA, $hashRefB) = @_;

    my %result = ();
    foreach my $key (keys %$hashRefA)
    {
        $result{$key} = 1;
    }
    foreach my $key (keys %$hashRefB)
    {
        $result{$key} = 1;
    }

    return \%result;
}

sub A_and_not_B($$)
{
    my ($hashRefA, $hashRefB) = @_;

    my %result = ();
    foreach my $key (keys %$hashRefA)
    {
        $result{$key} = 1 unless exists $hashRefB->{$key};
    }

    return \%result;
}

sub B_and_not_A($$)
{
    my ($hashRefA, $hashRefB) = @_;

    my %result = ();
    foreach my $key (keys %$hashRefB)
    {
        $result{$key} = 1 unless exists $hashRefA->{$key};
    }

    return \%result;
}

