The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use lib qw(t);
use Carp;
use Hash::AutoHash::AVPairsMulti;
use Test::More;
use Test::Deep;
use autohashUtil;

################################################################################
# SYNOPSIS
################################################################################
use Hash::AutoHash::AVPairsMulti;

# create object and set intial values
my $avp=new Hash::AutoHash::AVPairsMulti pets=>'Spot',hobbies=>'chess',hobbies=>'cooking';
cmp_autohash('create object and set intial values',$avp,
	     {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]});

# access or change hash elements via methods
my $pets=$avp->pets;		   # ['Spot']
cmp_deeply($pets,[qw(Spot)],'access via methods: single value scalar context');
my @pets=$avp->pets;		   # ['Spot']
cmp_deeply(\@pets,[qw(Spot)],'access via methods: single value array context');
my $hobbies=$avp->hobbies;	   # ['chess','cooking']
cmp_deeply($hobbies,[qw(chess cooking)],'access via methods: multiple values scalar context');
my @hobbies=$avp->hobbies;	   # ('chess','cooking')
cmp_deeply(\@hobbies,[qw(chess cooking)],'access via methods: multiple values array context');
$avp->hobbies('go','rowing');      # new values added to existing ones
cmp_autohash('update via methods',$avp,
	   {pets=>[qw(Spot)],hobbies=>[qw(chess cooking go rowing)]});
my $hobbies=$avp->hobbies;	   # ['chess','cooking','go','rowing']
cmp_deeply($hobbies,[qw(chess cooking go rowing)],'access via methods after update');
eval {$avp->family({kids=>'Joey'})};# illegal - reference
ok($@=~/Trying to store reference/,'ILLEGAL. reference');

# you can also use standard hash notation and functions
my($pets,$hobbies)=@$avp{qw(pets hobbies)};	# get 2 elements in one statement
cmp_deeply([$pets,$hobbies],
	   [[qw(Spot)],[qw(chess cooking go rowing)]],'access as hash: multiple values');
$avp->{pets}='Felix';	                 # set pets to ['Spot','Felix']   
cmp_autohash('update as hash',$avp,
	     {pets=>[qw(Spot Felix)],hobbies=>[qw(chess cooking go rowing)]});
my @keys=keys %$avp;		# ('pets','hobbies')
# NG 12-11-29: as of Perl 5.16 or so, the order of hash keys is randomized
# cmp_deeply(\@keys,[qw(pets hobbies)],'keys as hash');
cmp_set(\@keys,[qw(pets hobbies)],'keys as hash');
my @values=values %$avp;	# (['Spot','Felix'],
                                #  ['chess','cooking','go','rowing'])
# cmp_deeply(\@values,[[qw(Spot Felix)],[qw(chess cooking go rowing)]],'values as hash');
cmp_set(\@values,[[qw(Spot Felix)],[qw(chess cooking go rowing)]],'values as hash');

my(@keys,@values);		# NOT in docs. needed for testing
while (my($key,$value)=each %$avp) {
#  print "$key => @$value\n";	# prints each element as usual
  push(@keys,$key);		# NOT in docs. needed for testing
  push(@values,$value);		# NOT in docs. needed for testing
}
# NG 12-11-29: as of Perl 5.16 or so, the order of hash keys is randomized
# cmp_deeply(\@keys,[qw(pets hobbies)],'each as hash (keys)');
# cmp_deeply(\@values,[[qw(Spot Felix)],[qw(chess cooking go rowing)]],'each as hash (values)');
cmp_set(\@keys,[qw(pets hobbies)],'each as hash (keys)');
cmp_set(\@values,[[qw(Spot Felix)],[qw(chess cooking go rowing)]],'each as hash (values)');

delete $avp->{hobbies};	# no more hobbies
cmp_autohash('delete',$avp,{pets=>[qw(Spot Felix)]});

# CAUTION: hash notation doesn't respect array context!
$avp->{hobbies}=('go','rowing'); # sets hobbies to last value only
cmp_autohash('update as hash - does not handle lists',$avp,
	     {pets=>[qw(Spot Felix)],hobbies=>[qw(rowing)]});
my @hobbies=$avp->{hobbies};	    # @hobbies is (['rowing'])
cmp_deeply(\@hobbies,[[qw(rowing)]],'access as hash does not respect array context');

# alias $avp to regular hash for more concise hash notation
use Hash::AutoHash::AVPairsMulti qw(autohash_alias);
my %hash;
autohash_alias($avp,%hash);
cmp_autohash('autohash_alias',$avp,
	   {pets=>[qw(Spot Felix)],hobbies=>[qw(rowing)]},'hash',undef,\%hash);

# access or change hash elements without using ->
$hash{hobbies}=['chess','cooking']; # append values to hobbies 
cmp_autohash('update via alias',$avp,
	   {pets=>[qw(Spot Felix)],hobbies=>[qw(rowing chess cooking)]},'hash',undef,\%hash);
my $pets=$hash{pets};		    # ['Spot','Felix']
cmp_deeply($pets,[qw(Spot Felix)],'access via alias: name');
my $hobbies=$hash{hobbies};	    # ['go','chess','cooking']
cmp_deeply($hobbies,[qw(rowing chess cooking)],'access via alias: hobbies');
# another way to do the same thing
my($pets,$hobbies)=@hash{qw(pets hobbies)};
cmp_deeply([$pets,$hobbies],
	   [[qw(Spot Felix)],[qw(rowing chess cooking)]],'access via alias: multiple values');

# set 'unique' in tied object to eliminate duplicates
use Hash::AutoHash::AVPairsMulti qw(autohash_tied);
autohash_tied($avp)->unique(1);
$avp->hobbies('cooking','baking');
cmp_autohash('unique',$avp,
	   {pets=>[qw(Spot Felix)],hobbies=>[qw(rowing chess cooking baking)]},
	   'hash',undef,\%hash);
my @hobbies=$avp->hobbies;	# ('rowing','chess','cooking','baking')
cmp_deeply(\@hobbies,[qw(rowing chess cooking baking)],'access after unique');

################################################################################
# DESCRIPTION
################################################################################
#### Duplicate elimination and filtering
my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for unique',$avp,{hobbies=>[qw(go go)]});
my @hobbies=$avp->hobbies;         # ('go','go')
cmp_deeply(\@hobbies,[qw(go go)],'access duplicate values');

autohash_tied($avp)->unique(1);
cmp_autohash('setting unique removes duplicates',$avp,{hobbies=>[qw(go)]});
my @hobbies=$avp->hobbies;         # now ('go')
cmp_deeply(\@hobbies,[qw(go)],'access after setting unique');

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>['GO','go'];
cmp_autohash('create example for unique sub',$avp,{hobbies=>[qw(GO go)]});
autohash_tied($avp)->unique(sub {my($a,$b)=@_; lc($a) eq lc($b)});
cmp_autohash('setting unique sub removes case insensitve duplicates',$avp,{hobbies=>[qw(GO)]});
my @hobbies=$avp->hobbies;         # @hobbies is ('GO')
cmp_deeply(\@hobbies,[qw(GO)],'access after setting unique sub');

sub uniq_nocase_sort {
  my %uniq;
  my @values_lc=map { lc($_) } @_;
  @uniq{@values_lc}=@_;
  sort values %uniq;  
}

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>['GO','go','dance'];
cmp_autohash('example for filter sub',$avp,{hobbies=>[qw(GO go dance)]});
autohash_tied($avp)->filter(\&uniq_nocase_sort);
cmp_autohash('setting filter sub removes duplicates and sorts',$avp,{hobbies=>[qw(dance go)]});
my @hobbies=$avp->hobbies;	# @hobbies is ('dance','go')
cmp_deeply(\@hobbies,[qw(dance go)],'access after setting filter sub');

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>['GO','go','dance'];
cmp_autohash('example for filter one-liner',$avp,{hobbies=>[qw(GO go dance)]});
autohash_tied($avp)->filter(sub {my %u; @u{map {lc $_} @_}=@_; sort values %u});
cmp_autohash('filter one-liner removes duplicates and sorts',$avp,{hobbies=>[qw(dance go)]});

#### Functions and methods
$avp=new Hash::AutoHash::AVPairsMulti pets=>'Spot',hobbies=>'chess',hobbies=>'cooking';
cmp_autohash('new',$avp, {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]});
$avp=new Hash::AutoHash::AVPairsMulti [pets=>'Spot',hobbies=>'chess',hobbies=>'cooking'];
cmp_autohash('new ARRAY',$avp, {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]});
$avp=new Hash::AutoHash::AVPairsMulti {pets=>'Spot',hobbies=>['chess','cooking']};
cmp_autohash('new HASH',$avp, {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]});

## unique
my $boolean=1;
sub function1 {lc($_[0]) eq lc($_[1])}

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for unique form 1',$avp,{hobbies=>[qw(go go)]});
my $unique=tied(%$avp)->unique;
cmp_autohash('unique form 1',$avp,{hobbies=>[qw(go go)]});
ok(!$unique,'value returned by unique form 1');

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for unique form 2',$avp,{hobbies=>[qw(go go)]});
tied(%$avp)->unique($boolean);
cmp_autohash('unique form 2',$avp,{hobbies=>[qw(go)]});

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'chess',hobbies=>'GO',hobbies=>'go';
cmp_autohash('create example for unique form 3',$avp,{hobbies=>[qw(chess GO go)]});
tied(%$avp)->unique(\&function1);
cmp_autohash('unique form 3',$avp,{hobbies=>[qw(chess GO)]});

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for unique form 4',$avp,{hobbies=>[qw(go go)]});
my $unique=autohash_tied($avp)->unique;
cmp_autohash('unique form 4',$avp,{hobbies=>[qw(go go)]});
ok(!$unique,'value returned by unique form 4');

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for unique form 5',$avp,{hobbies=>[qw(go go)]});
autohash_tied($avp)->unique($boolean);
cmp_autohash('unique form 5',$avp,{hobbies=>[qw(go)]});

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'chess',hobbies=>'GO',hobbies=>'go';
cmp_autohash('create example for unique form 6',$avp,{hobbies=>[qw(chess GO go)]});
autohash_tied($avp)->unique(\&function1);
cmp_autohash('unique form 6',$avp,{hobbies=>[qw(chess GO)]});

## filter
my $boolean=1;
sub function2 {my %u; @_=map {lc $_} @_; @u{@_}=@_; values %u}

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for filter form 1',$avp,{hobbies=>[qw(go go)]});
my $filter=tied(%$avp)->filter;
cmp_autohash('filter form 1',$avp,{hobbies=>[qw(go go)]});
ok(!$filter,'value returned by filter form 1');

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for filter form 2',$avp,{hobbies=>[qw(go go)]});
tied(%$avp)->filter($boolean);
cmp_autohash('filter form 2',$avp,{hobbies=>[qw(go)]});

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'GO',hobbies=>'go';
cmp_autohash('create example for filter form 3',$avp,{hobbies=>[qw(GO go)]});
tied(%$avp)->filter(\&function2);
cmp_autohash('filter form 3',$avp,{hobbies=>[qw(go)]});

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for filter form 4',$avp,{hobbies=>[qw(go go)]});
my $filter=autohash_tied($avp)->filter;
cmp_autohash('filter form 4',$avp,{hobbies=>[qw(go go)]});
ok(!$filter,'value returned by filter form 4');

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'go',hobbies=>'go';
cmp_autohash('create example for filter form 5',$avp,{hobbies=>[qw(go go)]});
autohash_tied($avp)->filter($boolean);
cmp_autohash('filter form 5',$avp,{hobbies=>[qw(go)]});

my $avp=new Hash::AutoHash::AVPairsMulti hobbies=>'GO',hobbies=>'go';
cmp_autohash('create example for filter form 6',$avp,{hobbies=>[qw(GO go)]});
autohash_tied($avp)->filter(\&function2);
cmp_autohash('filter form 6',$avp,{hobbies=>[qw(go)]});

## functions inherited from Hash::AutoHash

use Hash::AutoHash::AVPairsMulti
  qw(autohash_alias autohash_tied autohash_get autohash_set
     autohash_clear autohash_delete autohash_each autohash_exists 
     autohash_keys autohash_values 
     autohash_count autohash_empty autohash_notempty);
my $avp=new Hash::AutoHash::AVPairsMulti pets=>'Spot',hobbies=>'chess',hobbies=>'cooking';
my(%hash,$tied,$result);
autohash_alias($avp,%hash);
cmp_autohash('create example for autohash_alias',$avp,
	   {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]},'hash',undef,\%hash);

$tied=autohash_tied($avp);
cmp_autohash('autohash_tied form 1',$avp,
	   {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]},'hash','object',\%hash,$tied);
$tied=autohash_tied(%hash);
cmp_autohash('autohash_tied form 2',$avp,
	   {pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]},'hash','object',\%hash,$tied);
$result=autohash_tied($avp,'FETCH','pets');
cmp_deeply($result,[qw(Spot)],'autohash_tied form 3');
$result=autohash_tied(%hash,'FETCH','pets');
cmp_deeply($result,[qw(Spot)],'autohash_tied form 4');

($pets,$hobbies)=autohash_get($avp,qw(pets hobbies));
cmp_deeply([$pets,$hobbies],[[qw(Spot)],[qw(chess cooking)]],'autohash_get');

autohash_set($avp,pets=>'Felix',new_key=>'Spot');
cmp_autohash('autohash_set',$avp,
	   {pets=>[qw(Spot Felix)],new_key=>[qw(Spot)],hobbies=>[qw(chess cooking)]});

autohash_clear($avp);
cmp_autohash('autohash_clear',$avp,{});

my $avp=new Hash::AutoHash::AVPairsMulti pets=>'Spot',new_key=>'Spot',hobbies=>[qw(chess cooking)];
cmp_autohash('create example again',$avp,
	   {pets=>[qw(Spot)],new_key=>[qw(Spot)],hobbies=>[qw(chess cooking)]});
my @keys=qw(pets hobbies);
autohash_delete($avp,@keys);
cmp_autohash('autohash_delete',$avp,{new_key=>[qw(Spot)]});

my $key='new_key';
if (autohash_exists($avp,$key)) {pass('autohash_exists')} else {fail('autohash_exists')}

my(@keys,@values);
my $avp=new Hash::AutoHash::AVPairsMulti pets=>'Spot',hobbies=>[qw(chess cooking)];
cmp_autohash('create example again',$avp,{pets=>[qw(Spot)],hobbies=>[qw(chess cooking)]});
while (my($key,$value)=autohash_each($avp)) { push(@keys,$key); push(@values,$value); }
# cmp_deeply(\@keys,[qw(pets hobbies)],'autohash_each form 1 (keys)');
# cmp_deeply(\@values,[[qw(Spot)],[qw(chess cooking)]],'autohash_each form 1 (values)');
cmp_set(\@keys,[qw(pets hobbies)],'autohash_each form 1 (keys)');
cmp_set(\@values,[[qw(Spot)],[qw(chess cooking)]],'autohash_each form 1 (values)');
my(@keys,@values);
while (my $key=autohash_each($avp)) { push(@keys,$key); }
# cmp_deeply(\@keys,[qw(pets hobbies)],'autohash_each form 2 (keys)');
cmp_set(\@keys,[qw(pets hobbies)],'autohash_each form 2 (keys)');

my(@keys,@values);
@keys=autohash_keys($avp);
# cmp_deeply(\@keys,[qw(pets hobbies)],'autohash_keys');
cmp_set(\@keys,[qw(pets hobbies)],'autohash_keys');
@values=autohash_values($avp);
# cmp_deeply(\@values,[[qw(Spot)],[qw(chess cooking)]],'autohash_values');
cmp_set(\@values,[[qw(Spot)],[qw(chess cooking)]],'autohash_values');

my $count;
$count=autohash_count($avp);
is($count,2,'autohash_count');

if (autohash_empty($avp)) {fail('autohash_empty')} else {pass('autohash_empty')}
if (autohash_notempty($avp)) {pass('autohash_empty')} else {fail('autohash_empty')}

done_testing();