#!./perl

use strict;
use Encode;
use Benchmark qw(:all);

my $Count = shift @ARGV;
$Count ||= 16;
my @sizes = @ARGV || (1, 4, 16);

my %utf8_seed;
for my $i (0x00..0xff){
    my $c = chr($i);
    $utf8_seed{BMP} .= ($c =~ /^\p{IsPrint}/o) ? $c : " ";
}
utf8::upgrade($utf8_seed{BMP});

for my $i (0x00..0xff){
    my $c = chr(0x10000+$i);
    $utf8_seed{HIGH} .= ($c =~ /^\p{IsPrint}/o) ? $c : " ";
}
utf8::upgrade($utf8_seed{HIGH});

my %S;
for my $i (@sizes){
    my $sz = 256 * $i;
    for my $cp (qw(BMP HIGH)){
	$S{utf8}{$sz}{$cp}  = $utf8_seed{$cp} x $i;
	$S{utf16}{$sz}{$cp} = encode('UTF-16BE', $S{utf8}{$sz}{$cp});
    }
}

for my $i (@sizes){
    my $sz = $i * 256;
    my $count = $Count * int(256/$i);
    for my $cp (qw(BMP HIGH)){
	for my $op (qw(encode decode)){
	    my ($meth, $from, $to) = ($op eq 'encode') ?
		(\&encode, 'utf8', 'utf16') : (\&decode, 'utf16', 'utf8');
	    my $XS = sub {
		Encode::Unicode::set_transcoder("xs");  
		$meth->('UTF-16BE', $S{$from}{$sz}{$cp})
		     eq $S{$to}{$sz}{$cp} 
			 or die "$op,$from,$to,$sz,$cp";
	    };
	    my $modern = sub {
		Encode::Unicode::set_transcoder("modern");  
		$meth->('UTF-16BE', $S{$from}{$sz}{$cp})
		     eq $S{$to}{$sz}{$cp} 
			 or die "$op,$from,$to,$sz,$cp";
	    };
	    my $classic = sub {
		Encode::Unicode::set_transcoder("classic");  
		$meth->('UTF-16BE', $S{$from}{$sz}{$cp})
		     eq $S{$to}{$sz}{$cp} or 
			 die "$op,$from,$to,$sz,$cp";
	    };
	    print "---- $op length=$sz/range=$cp ----\n";
	    my $r = timethese($count,
		     {
		      "XS"      => $XS,
		      "Modern"  => $modern,
		      "Classic" => $classic,
		     },
		     'none',
		    );
	    cmpthese($r);
	}
    }
}
