use Test::More qw(no_plan);

use Math::Cartesian::Product;
use warnings FATAL => qw(all);
use strict;

# 0

ok 0 == cartesian {};

# 3

 {my $a = '';
  
  ok 0 == cartesian {0}               [qw(a b c)];
  ok 1 == cartesian {shift() eq 'b'}  [qw(a b c)];
  ok 2 == cartesian {shift() ne 'b'}  [qw(a b c)];
  ok 3 == cartesian {$a .= "@_\n"; 1} [qw(a b c)];
  ok $a eq << "end";
a
b
c
end
 }

# 3*2

 {my $a = '';
  ok 6 == cartesian {$a .= "@_\n"; 1} [qw(a b c)], [1,2];
  ok $a eq << "end";
a 1
a 2
b 1
b 2
c 1
c 2
end
 }

# 3*2*0

 {my $a = '';
  
  ok 0 == cartesian {$a .= "@_\n"; 1} [qw(a b c)], [1,2], [];
  ok $a eq << "end";
end
 }


# 2*2*2*2

 {my $a = '';
  my $b = [qw(a b)];
  ok 16 == cartesian {$a .= "@_\n"; 1} $b,$b,$b,$b;
  ok $a eq << "end";
a a a a
a a a b
a a b a
a a b b
a b a a
a b a b
a b b a
a b b b
b a a a
b a a b
b a b a
b a b b
b b a a
b b a b
b b b a
b b b b
end
 }

# (2*2)*(2*2)

 {my $a = '';
  my $b = [qw(a b)];
  my $c = [cartesian {$a .= "@_\n"; 1} $b,$b];
  ok 4 == @$c;
  ok $a eq << "end";
a a
a b
b a
b b
end

  my $d = '';
  ok 16 == cartesian {$d .= "@_\n"; 1} $c, $c;
  ok $d eq << "end";
a a a a
a a a b
a a b a
a a b b
a b a a
a b a b
a b b a
a b b b
b a a a
b a a b
b a b a
b a b b
b b a a
b b a b
b b b a
b b b b
end

  ok 8 == cartesian {$_[1] eq 'a'} $c, $c;
 }


# 40*40*40*40

 {my $a = [1..40];
  ok 2_560_000 == cartesian {1} $a,$a,$a,$a;
 }
 
# Tests from Philipp Rumpf 
 
# The empty product contains one element, the empty list

 {my $a = '';
  ok 1 ==  cartesian { $a .= "@_\n" };
  ok $a eq "\n";
 }
 
 {ok 0 ==  @{[cartesian {1}]->[0]};
  ok defined [cartesian {1}]->[0];
  ok ref([cartesian {1}]->[0]) =~ /Math::Cartesian::Product/;
  ok defined([cartesian {1}]->[0]);
 }

# Including the empty set in the product list produces the empty set

 {my $a = '';
  ok 0 == cartesian { $a .= "@_\n" } [];
  ok $a eq '';
 }

 {my $a = '';
  ok 0 == cartesian { $a .= "@_\n" } [], [];
  ok $a eq '';
 }

 {my $a = '';
  ok 0 == cartesian { $a .= "@_\n" } [], [], [];
  ok $a eq '';
 }
 {my $a = '';
  ok 0 == cartesian { $a .= "@_\n" } [1,2,3], [];
  ok $a eq '';
 }

 {my $a = '';
  ok 0 == cartesian { $a .= "@_\n" } [], [1,2,3];
  ok $a eq '';
 }

 {my $a = '';
  ok 0 == cartesian { $a .= "@_\n" } [], [1,2,3], [];
  ok $a eq '';
 }


# Cartesian products split, so the cartesian product of (@a,@b) can be
# achieved by taking the cartesian product of @a inside the cartesian
# product of @b.

 {my @a = ([1,2]);
  my @b = ([3,4]);

  my $a = '';
  ok 2 == cartesian { my @c = @_; cartesian { my @d = (@c, @_); $a .= "@d\n"} @b } @a;

  my $b = '';
  ok 4 == cartesian { $b .= "@_\n"} @a, @b;

  ok $a eq $b;
 }

# Cartesian products split even when the first list is empty

 {my @a = ();
  my @b = ([2,3]);

  my $a = '';
  ok 1 == cartesian { my @c = @_; cartesian { my @d = (@c, @_); $a .= "@d\n"} @b } @a;

  my $b = '';
  ok 2 == cartesian { $b .= "@_\n"} @a, @b;

  ok $a eq $b;
 }

# Cartesian products split even when the second list is empty

 {my @a = ([1,2]);
  my @b = ();

  my $a = '';
  ok 2 == cartesian { my @c = @_; cartesian { my @d = (@c, @_); $a .= "@d\n"} @b } @a;

  my $b = '';
  ok 2 == cartesian { $b .= "@_\n"} @a, @b;

  ok $a eq $b;
 }

# Exponentiation can be performed using the cartesian product

 {my @a = ([1,2]);
  ok 2**$_ == cartesian {1} ((@a) x $_) for 0..5;
 }
