# -*- Mode: CPerl -*-
# t/01_distance.t: test edit distance

$TEST_DIR = './t';
#use lib qw(../blib/lib ../blib/arch); $TEST_DIR = '.'; # for debugging

# load common subs
use Test;
do "$TEST_DIR/common.plt";
use PDL;
use PDL::EditDistance;

BEGIN { plan tests=>29, todo=>[]; }

##---------------------------------------------------------------------
## 1..3: _edit_pdl()
sub test_edit_pdl {
  our $s = 'ABC';
  our $l = [map { ord($_) } split(//,$s)];
  our $p = pdl(byte,$l);
  our $s_pdl = PDL::EditDistance::_edit_pdl($s);
  our $l_pdl = PDL::EditDistance::_edit_pdl($l);
  our $p_pdl = PDL::EditDistance::_edit_pdl($p);
  our $pdl_want = pdl [0,65,66,67];
  isok("_edit_pdl(string) : ", all($s_pdl==$pdl_want));
  isok("_edit_pdl(array ) : ", all($l_pdl==$pdl_want));
  isok("_edit_pdl(pdl   ) : ", all($p_pdl==$pdl_want));
}
test_edit_pdl();

##---------------------------------------------------------------------
## util: makepdls
sub makepdls {
  ($s1,$s2) = ('GUMBO','GAMBOL');
  our $a = pdl(byte,[map { ord($_) } split(//,$s1)]);
  our $b = pdl(byte,[map { ord($_) } split(//,$s2)]);
  our $a1 = $a->flat->reshape($a->nelem+1)->rotate(1);
  our $b1 = $b->flat->reshape($b->nelem+1)->rotate(1);
}


##---------------------------------------------------------------------
## 4..6: edit_costs_static()
sub test_edit_costs_static {
  makepdls;
  our ($costsMatch,$costsIns,$costsSubst) = edit_costs_static(long,$a->nelem,$b->nelem, 0,1,2);
  $costsMatch_want = zeroes(byte,$a->nelem+1,$b->nelem+1);
  $costsIns_want   = zeroes(byte,$a->nelem+1,$b->nelem+1) +1;
  $costsSubst_want = zeroes(byte,$a->nelem+1,$b->nelem+1) +2;
  isok("costs_static: match: ", all($costsMatch==$costsMatch_want));
  isok("costs_static:   ins: ", all($costsIns==$costsIns_want)    );
  isok("costs_static: subst: ", all($costsSubst==$costsSubst_want));
}
test_edit_costs_static();


##---------------------------------------------------------------------
## 7..8: test_distance_full: distance matrix full
sub test_distance_full {
  makepdls;
  our @costs   =  edit_costs_static(double, $a->nelem,$b->nelem, 0,1,1);
  our $dmat    = _edit_distance_full($a1,$b1,@costs);
  our $dmat2   =  edit_distance_full($a,$b,@costs);
  our $dmat_want = pdl([
			[0, 1, 2, 3, 4, 5],
			[1, 0, 1, 2, 3, 4],
			[2, 1, 1, 2, 3, 4],
			[3, 2, 2, 1, 2, 3],
			[4, 3, 3, 2, 1, 2],
			[5, 4, 4, 3, 2, 1],
			[6, 5, 5, 4, 3, 2],
		       ]);
  isok("_edit_distance_full: ", all($dmat==$dmat_want) );
  isok("edit_distance_full : ", all($dmat2==$dmat_want) );
}
test_distance_full;

##---------------------------------------------------------------------
## 9..10: test_distance_static: distance matrix, static
sub test_distance_static {
  makepdls;
  @costs   = map { pdl(double,$_) } (0,1,1);
  $dmat    = _edit_distance_static($a1,$b1,@costs);
  $dmat2   =  edit_distance_static($a,$b,@costs);
  our $dmat_want = pdl([
			[0, 1, 2, 3, 4, 5],
			[1, 0, 1, 2, 3, 4],
			[2, 1, 1, 2, 3, 4],
			[3, 2, 2, 1, 2, 3],
			[4, 3, 3, 2, 1, 2],
			[5, 4, 4, 3, 2, 1],
			[6, 5, 5, 4, 3, 2],
		       ]);
  isok("_edit_distance_static: ", all($dmat==$dmat_want));
  isok("edit_distance_static : ", all($dmat2==$dmat_want));
}
test_distance_static;


##---------------------------------------------------------------------
## 11..14: test_align: alignment matrix
sub test_align_full {
  makepdls;
  our @costs = edit_costs_static(double, $a->nelem,$b->nelem, 0,1,1);
  our ($dmat,$amat)  = _edit_align_full($a1,$b1,@costs);
  our ($dmat2,$amat2) =  edit_align_full($a,$b,@costs);
  our $dmat_want = pdl([
			[0, 1, 2, 3, 4, 5],
			[1, 0, 1, 2, 3, 4],
			[2, 1, 1, 2, 3, 4],
			[3, 2, 2, 1, 2, 3],
			[4, 3, 3, 2, 1, 2],
			[5, 4, 4, 3, 2, 1],
			[6, 5, 5, 4, 3, 2],
		       ]);
  our $amat_want = pdl [
			[0, 1, 1, 1, 1, 1],
			[2, 0, 1, 1, 1, 1],
			[2, 2, 3, 3, 3, 3],
			[2, 2, 3, 0, 1, 1],
			[2, 2, 3, 2, 0, 1],
			[2, 2, 3, 2, 2, 0],
			[2, 2, 3, 2, 2, 2],
		       ];

  isok("_edit_align_full (dist) : ", all($dmat==$dmat_want) );
  isok("_edit_align_full (align): ", all($amat==$amat_want) );
  isok("edit_align_full  (dist) : ", all($dmat2==$dmat_want) );
  isok("edit_align_full  (align): ", all($amat2==$amat_want) );
}
test_align_full;


##---------------------------------------------------------------------
## 15..18: test_align_static: alignment matrix, static
sub test_align_static {
  makepdls;
  our @costs = (0,1,1);
  our ($dmat,$amat)   = _edit_align_static($a1,$b1,@costs);
  our ($dmat2,$amat2) =  edit_align_static($a,$b,@costs);
  our $dmat_want = pdl([
			[0, 1, 2, 3, 4, 5],
			[1, 0, 1, 2, 3, 4],
			[2, 1, 1, 2, 3, 4],
			[3, 2, 2, 1, 2, 3],
			[4, 3, 3, 2, 1, 2],
			[5, 4, 4, 3, 2, 1],
			[6, 5, 5, 4, 3, 2],
		       ]);
  our $amat_want = pdl [
			[0, 1, 1, 1, 1, 1],
			[2, 0, 1, 1, 1, 1],
			[2, 2, 3, 3, 3, 3],
			[2, 2, 3, 0, 1, 1],
			[2, 2, 3, 2, 0, 1],
			[2, 2, 3, 2, 2, 0],
			[2, 2, 3, 2, 2, 2],
		       ];

  isok("_edit_align_static (dist) : ", all($dmat==$dmat_want) );
  isok("_edit_align_static (align): ", all($amat==$amat_want) );
  isok("edit_align_static  (dist) : ", all($dmat2==$dmat_want) );
  isok("edit_align_static  (align): ", all($amat2==$amat_want) );
}
test_align_static;

##---------------------------------------------------------------------
## 19..21 test_bestpath: best path
sub test_bestpath {
  makepdls;
  @costs = (0,1,1);
  ($dmat,$amat) = edit_align_static($a,$b,@costs);
  our ($apath,$bpath,$pathlen) = edit_bestpath($amat);
  our $pathlen_want = 6;
  our $apath_want = pdl [0, 1, 2, 3, 4, 4];
  our $bpath_want = pdl [0, 1, 2, 3, 4, 5];
  isok("bestpath: len  : ", $pathlen==$pathlen_want );
  isok("bestpath: apath: ", all($apath->slice("0:".($pathlen-1))==$apath_want) );
  isok("bestpath: bpath: ", all($bpath->slice("0:".($pathlen-1))==$bpath_want) );
}
test_bestpath;

##---------------------------------------------------------------------
## 22..25 test_pathtrace: full path backtrace
sub test_pathtrace {
  makepdls;
  @costs = (0,1,1);
  ($dmat,$amat) = edit_align_static($a,$b,@costs);
  our ($ai,$bi,$ops,$len) = edit_pathtrace($amat);
  our $len_want = 6;
  our $ai_want  = pdl [1,2,3,4,5,5];
  our $bi_want  = pdl [1,2,3,4,5,6];
  our $ops_want = pdl [0,3,0,0,0,2]; ##-- match, subst, match, match, match, insert2
  isok("pathtrace: len : ", $len==$len_want );
  isok("pathtrace:  ai : ", all($ops==$ops_want));
  isok("pathtrace:  bi : ", all($ops==$ops_want));
  isok("pathtrace: ops : ", all($ops==$ops_want));
}
test_pathtrace;

##---------------------------------------------------------------------
## 26..29 test_lcs: test LCS
sub test_lcs {
  my $a = pdl(long,[0,1,2,3,4]);
  my $b = pdl(long,[  1,2,1,4,0]);
  my $lcs = edit_lcs($a,$b);
  my ($ai,$bi,$len) = lcs_backtrace($a,$b,$lcs);
  my $lcs_want = pdl(long, [[0,0,0,0,0,0],
			    [0,0,1,1,1,1],
			    [0,0,1,2,2,2],
			    [0,0,1,2,2,2],
			    [0,0,1,2,2,3],
			    [0,1,1,2,2,3]]);
  my $ai_want = pdl(long,[1,2,4]);
  my $bi_want = pdl(long,[0,1,3]);
  my $len_want = 3;
  isok("lcs: matrix : ", ($lcs==$lcs_want)->all);
  isok("lcs: len    : ", $len==$len_want);
  isok("lcs: ai     : ", ($ai==$ai_want)->all);
  isok("lcs: bi     : ", ($bi==$bi_want)->all);
}
test_lcs();

print "\n";
# end of t/01_distance.t

