#!/usr/bin/perl -w

@Canines{ qw(dog wolf) } = ( );
@Domesticated{ qw(cat dog horse) } = ( );

( undef, $numbers, $names ) = members_to_numbers( \%Domesticated, \%Canines );

$Domesticated = hash_set_to_bit_vector( \%Domesticated, $numbers );
$Canines = hash_set_to_bit_vector( \%Canines, $numbers );

# Binary mask is AND with NOT.
$difference           = $Canines & ~$Domesticated;

# Binary XOR.
$symmetric_difference = $Canines ^  $Domesticated;

print "difference = ",
      "@{[keys %{bit_vector_to_hash_set( $difference, $names )}]}\n";
print "symmetric_difference = ",
      "@{[keys %{bit_vector_to_hash_set( $symmetric_difference,
                                       $names )}]}\n";

sub bit_vector_to_hash_set {
    my ( $vector, $names ) = @_;
    my ( $number, %hash_set );

    foreach $number ( 0..$#{ $names }) {
        $hash_set{ $names->[ $number ] } = undef
            if vec( $vector, $number, 1 );
    }

    return \%hash_set;
}

sub hash_set_to_bit_vector { my ( $hash, $numbers ) = @_; my ( $name,
    $vector );

    # Initialize $vector to zero bits.
    #
    $vector = '';

    while ( defined ($name = each %{ $hash })) {
        vec( $vector, $numbers->{ $name }, 1 ) = 1;
    }

    return $vector;
}

sub members_to_numbers {
    my ( @names,   $name );
    my ( %numbers, $number );

    $number = 0;
    while ( my $set = shift @_ ) {
        while ( defined ( $name = each %$set ) ) {
            unless ( exists $numbers{ $name } ) {
		$numbers{ $name   } = $number;
		$names  [ $number ] = $name;
		$number++;
            }
        }
    }

    return ( $number, \%numbers, \@names );
}

