#!/usr/bin/env perl
# altparsnpce.pl
# evigene/scripts/genes/altparsnpdist.pl 
# 2022/Nov dgg, see tr2dupgeneclass1a.pl

=item altparsnpce

  measure alt/par evidence of EVg groups (pubid main/alt "loci")
  a. from mult aligns of cds (using muscle -in evgroupNNNN.cds -out evgroupNNNN.msa )
     .. pairwise align diffs (snp-dif), stats show var(snp) is useful alt/par evd,
     .. combined w/ "spred" of snp-dif, ie snp found over span of align, not just one/two spots
        alts can differ at two ends, identical over main middle, or other like pattern w/ large identical span
        "good" pars have snp single nucl dif scattered over span

  b. from distance tree of mult aligns, (using muscle -in evgroupNNNN.msa -maketree -out evgroupNNNN.tre )
     given MIN_ALT_IDENT (or max distance) cutoff, common branches in tree separate paralogs, w/ alts in each apcut-branch
     Picking cut point for alt/par distance is problem as alts and pars exist in same idenity range (100..90)
     
  Added dna-copynum evidence from gnodes genexcopy (nparalog) aids choice of cut points for a,b.
  Each measure is not reliable for all gene alt/par groups; Combining three measures carefully can improve this
  
=item notes

  trial notes in altparsnpce_eqgenetry1a.txt

  .. how to combine evsim (tree common branch cuts) & snp_var ?
   -- require both pass cutoff to call par from alt?
   .. problem w/ tree cutoffs: dist values are *not* pct-ident vals; replace w/ snp Nd/Nc?

 ** Cmpare ref-diff from adupfilt4d/ phiid4d_evsim w/ refdif of altparsnpce4e 
   n=111 in adupfilt4d/at18tair_mrna_phiid4d_evsim_refdiff.list, how many Altbad,Parbad not in altparsnpce4e.refdif?
   .. does altparsnpce4e add useful snpdist filter to phiid4d_evsim?
   .. can evsim global ident + snpdist spread call alt/par w/o dna dupx evidence? No, not reliably;

 >> mix of all 3 dupx evd (dna-tcopy,cds-global-ident,cds-snpvar) sources *might* work out, if properly balanced
     a. evgroup dna-tcopy : 1 (likely join) vs 2+ (likely split)
         .. not all dna-copy seqs in evgroup, and/or evgroup can contain distant parlogs missing dna-xcopy
     b. evgroup global ident (aln-tree-dist) : >99.0(likely join), 99-90 (other evd), <90 (must split)
         .. alts and pars share range in global align idents 90..99+ (including 100% ident)
     c. evgroup snp-vardist (altparsnp..) : <10 (likely join), 10-25 (other evd), >25 (likely split)
	 .. snp-var metric can distinguish alts from pars, but not always reliable

  # 45 in evsim, not in snpce
  comm -23 adupfilt4d/at18tair_mrna_phiid4d_evsim_refdiff.ids at18phiid4d_altparsnpce4e_refdiff.ids | wc -l = 45
  # 168 not in evsim, in snpce
  comm -13 adupfilt4d/at18tair_mrna_phiid4d_evsim_refdiff.ids at18phiid4d_altparsnpce4e_refdiff.ids | wc -l = 168
  # 66 in both

=item test examples evsim(tree) & snp_dist

  $evigene/scripts/genes/tr2dupgeneclass1a.pl -trset at18tair_mrna -refids at18tair_mrna.gidtab -cds inputset/at18tair_mrna.cds  -debug  -pubids NONE \
   -eqgenetab  adupfilt4d/at18tair_mrna_phiid4d_evsim.eqgene

  #tr2dgc: checkref_eqgene(adupfilt4d/at18tair_mrna_phiid4d_evsim.eqgene,at18tair_mrna.gidtab)
  #tr2dgc: checkref_eqgene ngroup=8353, ngsame=8242, ngdif=111, ntr=21466, nsame=42130, ndif=580, nmiss=0
  #tr2dgc: checkref_eqgene ngaltn=8066, ngparn=346, ngaltonly=8007, 
    NTaltok=40562, NTaltbad=426, %Altbad=1.0;   
    NTparok=1568, NTparbad=154, %Parbad=8.9
  #tr2dgc: checkref_eqgene ref-diff groups: 15:tmps/EVGm000023.mutree, 87:tmps/EVGm000128.mutree, 148:tmps/EVGm000228.mutree, 216:tmps/EVGm000341.mutree, 279:tmps/EVGm000457.mutree, 327:tmps/EVGm000545.mutree, 339:tmps/EVGm000564.mutree, 369:tmps/EVGm000615.mutree, 521:tmps/EVGm000928.mutree, 531:tmps/EVGm000941.mutree, .. n=111 in adupfilt4d/at18tair_mrna_phiid4d_evsim_refdiff.list
  
  $evigene/scripts/genes/tr2dupgeneclass1a.pl -trset at18tair_mrna -refids at18tair_mrna.gidtab -cds inputset/at18tair_mrna.cds  -debug  -pubids NONE  -eqgenetab  at18phiid4d_altparsnpce2b.eqgene
  
  #tr2dgc: checkref_eqgene(at18phiid4d_altparsnpce2b.eqgene,at18tair_mrna.gidtab)
  #tr2dgc: checkref_eqgene ngroup=8370, ngsame=7982, ngdif=370, ntr=21476, nsame=41151, ndif=1528, nmiss=24519
  #tr2dgc: checkref_eqgene ngaltn=8052, ngparn=707, ngaltonly=7663, 
    NTaltok=39417, NTaltbad=260, %Altbad=0.7;   << lower
    NTparok=1734, NTparbad=1268, %Parbad=42.2   << very high
  #tr2dgc: checkref_eqgene ref-diff groups: 51:tmps4d/EVGm000078.msa, 58:tmps4d/EVGm000088.msa, 73:tmps4d/EVGm000109.msa, 82:tmps4d/EVGm000121.msa, 83:tmps4d/EVGm000122.msa, 102:tmps4d/EVGm000156.msa, 136:tmps4d/EVGm000207.msa, 163:tmps4d/EVGm000246.msa, 177:tmps4d/EVGm000273.msa, 181:tmps4d/EVGm000281.msa, .. n=370 in at18phiid4d_altparsnpce2b_refdiff.list

  .. reduce spurious snpvar par calls, use other stats to boost/change snpvar cut: 
   a. Nd-low (<10?) ~= pD2sn low (< 0.01/0.001?),
   b. skew? abs(mD2sn - aD2sn) >= 30?
   c. check better D2span for few outliers, eg: 1, 97, 98, 99, 99; 0.1,1,2,2,3, 99
   d. maybe reuse snp = eq(i-1),ne(i),eq(i+1) singletons, drops out large-span difs (exon like)


=cut

use strict;
use constant UPD22DEC23 => 1; # revise move alt2par, par2alt based on tests. 
use constant UPD23JAN01 => 1; # snp dist,tree output table: otype=vals?, modes for/from decision tree stats
use constant UPD23JAN17 => 1; # fold in apclass_idxdnacn.pl idmax x npar classifier, simple, ~best
use constant UPD23FEB20 => 1; # lastz pairalign, antisense/orient val = paralog

my $debug=$ENV{debug}||0;
my $nparalog=$ENV{nparalog}||0; # genexcopy.tcopy == nparalog; from tr2dupgeneclass1a.pl

my $otype= $ENV{otype}||0; #? drop, eqgene now always, others debug out
if($otype) { 
  $otype=($otype =~ /^s/i)?'sim':($otype=~/^d/i)?'dist':($otype=~/^e/i)?'eqgene':($otype=~/^v/i)?'vals':'dist'; 
} else {
  $otype= $ENV{sim}?'sim':'dist'; # distance or sim matrix output : drop these?
  $otype='eqgene' if($ENV{eqgene}); #? drop
  #UPD23jan: vals output of classify_ reuse dist/sim versus eqgene output
}

my $PVALfmt= $ENV{pvalfmt} || $ENV{noval} || 0; # eqgene val format
$PVALfmt=1 if($PVALfmt=~/^no/i);
$PVALfmt=2 if($PVALfmt=~/^short|brief/i);
$PVALfmt=0 if($PVALfmt=~/^long/i);

# classify_ relies on this ispar val .. modify? 
# UPD23JAN17  use global const: ISPAR_DUNNO; ISPAR_CERT/LIKE/MAYBE? ISALT_CERT/LIKE/MAY
use constant { cALT => -1, cPAR => 1, cUNK => 0, cMISS => 0, };

# use constant { # const compare bugs here ???
#   ISPAR_CERT => 3, ISPAR_LIKE => 2, ISPAR_MAY => 1, # par_
#   ISALT_CERT => -3, ISALT_LIKE => -2, ISALT_MAY => -1, # alt_
#   ISPAR_DUNNO => 0, #? or ISUNK unk_ or -666 ?
#   };

use constant { SNP_LOWN => 5, SNP_HISKEW => 30, NPARDUP => 1.5, }; # snpdist_pairs
use constant { SNP_IDMAX_ISPAR => 35, SNP_IDMAX_ISALT => 85, SNP_IDMAX_NPARCUT => 55, }; # NOT const, 
        # was SNP_LOWN => 9 ; drop down as 5/1000 seems valid measure, 1..3 can give spurious results
my($IDX_PAR,$IDX_ALT,$IDX2CN)=(SNP_IDMAX_ISPAR,SNP_IDMAX_ISALT,SNP_IDMAX_NPARCUT); # 55 best? idx2cn 85,35 == max,min, weed,hum differ on best

my $ADDMUTES= $ENV{murate}||0;  #UPD23Jan06: add random err to bases, as pct, use murate=0.1 .. 0.5 .. 1 max

## classify_pairs opts:
## make this default: $ENV{moveparcb} seems to work ok now; turnoff= $ENV{nomoveparcb}
## revise these: make cbmovealt2par default off, sep opt for cbmovepar2alt
my $DOcbmovepar2alt= $ENV{cbmovepar2alt} ? 1 : $ENV{nomoveparcb} ? 0 : 0; # for npar==1, def: off
my $DOcbmovealt2par= $ENV{cbmovealt2par} ? 1 : $ENV{nomoveparcb} ? 0 : 0; # for npar>=2, def: off
#UPD22DEC23 old, drop these
my $DOmoveparcb   = $ENV{nomoveparcb} ? 0 : $ENV{moveparcb} ? 1 : 0; #was $ENV{moveparcb}
my $DOmovepar2alt = $ENV{nomovepar2alt} ? 0 : not $DOmoveparcb; # old way .. drop?
my $DOmovealt2par = $ENV{movealt2par} ? not $DOmoveparcb : 0; # drop?

#** need to measure params snpvar, maxsnpvar, pminminalt for true/false alt/par effects
my $MINPAR_snpvar = $ENV{snpvar} || 10; # use with nparalog > 1, needs testing, cutoff for alt/par eqgene table
my $MAXMAXALT_snpvar = $ENV{maxsnpvar} || 20; #or 25; # use with nparalog=1, dont call pars unless snpvar very high .. some alts have snpvar >10, most<25?
# MINPAR_snpvar == MAXALT_snpvar

my $SNP1in3= $ENV{snp1in3} || 0; # dont use, not helpful
my %BASEOK= map{ $_,1 } qw(A C G T a c g t);
my $GAPOK= (defined $ENV{gapok}) ? $ENV{gapok}: undef; # count/not align-gaps for measures: BASEOK{'-'}=1;
   # likely want GAPOK on for blast/exonr in, not msa;
   
my $BLASTIN= $ENV{blastin}||0; # blastn -query $pt.cds -subject $pt.cds -outfmt 3 << '.' BASEOK of fmt3 = multalign
# ^^ FIXME $BLASTIN=3 for fmt3 multalign, $BLASTIN=1 for fmt0 pairaln, diff reader..
my $EXONRIN= $ENV{exonr}||0; # fixme, getopts() ?
my $AXTIN=  $ENV{axt}||0; # add aligner=lastz/other --format=axt
my $ALIGNER= $ENV{'aligner'};
unless($ALIGNER) { $ALIGNER= ($AXTIN)? 'alastz' : ($EXONRIN)? 'exonerate' : ($BLASTIN)?'blast' : ''; } # '' or 'muscle' ??

my $doKURT= $ENV{kurt}||0; # was 2; drop ? not useful
# my $doKAKS= $ENV{kaks}?1:0; # drop as opt
# my $TYDIST= $ENV{tydist} || 2; # fixed TYDIST=2 now for snp_distke()

## tree alt/par cuts
#  my $cmd="env prt=1 nparalog=$npar otype=eg $EVIGENES/omcl/newickparse.pl $otre > $oegtab";
my $MINALT_tree=$ENV{pminalt}||98.0; #was 99.0, too high?  
my $MIN_MINALT_tree=$ENV{pminminalt}|| 90.0; #was 97.0; # force paralog at lower idents, dunno what default, 95..98 
$MIN_MINALT_tree= _min($MIN_MINALT_tree, $MINALT_tree - 0.1); #?? not -2

my $iname=  $ENV{inmsa} || $ARGV[0] ||"stdin"; # evgroup_cds.msa mult seq align here
my $intree= $ENV{intree} || $ARGV[1] || "";
unless($intree) { # try from iname == evgroup.msa
  my $it=$iname; $it =~ s/\.\w+$//;   
  for my $sf (qw(tre tree nwk mutree)) { if(-s "$it.$sf") { $intree= "$it.$sf"; last; } }
}

my $ingenexf = $ENV{genex}||""; #UPD23JAN17;
my $evgroupId= $ENV{evgroup}||""; #UPD23JAN17 : iname is backup, use for reclassed alt/par pubids
if(not $evgroupId and $iname and not($iname =~ /stdin/)) {
  # #iname=tmps4d/EVGm000088.msa : pick EVgroup id
  my $d= $iname; my $i=rindex($d,'/'); if($i >= 0){ $d=substr($d,$i+1); } $d=~s/\.\w+$//; 
  $evgroupId= $d;
}

sub MAIN_stub {}
print "#iname=$iname\n"; # intree also?
print "#altpar_snp_params: nparalog=$nparalog, snpvar=$MINPAR_snpvar (np>1) .. $MAXMAXALT_snpvar (np==1)\n"; # MAXMAXALT_snpvar
print "#altpar_simpleclass: apclass=(idmax>=$IDX_ALT)?alt:(idmax<=$IDX_PAR)?par:(dnacn>=1.5)?par:alt \n";
print "#altpar_tree_params: ident_altmin=$MINALT_tree (np>1), ident_parmust=$MIN_MINALT_tree (np==1)\n"; 

my($ngenex,$genexcopy,$genesize)=  ($ingenexf)? read_genexcopy($ingenexf) : (0); #UPD23JAN17

my @atreedist_keys=(); # global set by parseAlnTree = value keys in atreedist->{id}{jd}{key}
my($nid_atree, $atreedist, $atreeval, $atreeroot, $atreenodes, $atreenodea)= ($intree)? parseAlnTree($intree) : (0); # file not stdin

my($nid_aln,$alnseq)=(0);
my($inh); if($iname =~ /^(stdin|\-)/i) { $inh=*STDIN; } else { open($inh,$iname); }  

# test 1st line? 
my $intop="";
unless($ALIGNER){
  $intop= <$inh>;  # need to push-back >seqid
  if($intop =~/BLAST/){ $BLASTIN=1; $ALIGNER='b'; } 
  elsif($intop =~/exonerate/){ $EXONRIN=1; $ALIGNER='e'; } 
  elsif($intop =~/axt|lastz/i){ $AXTIN=1; $ALIGNER='a'; } 
  elsif($intop =~/^>/){ $ALIGNER='m'; }
}

if($BLASTIN) {  
  $GAPOK=1 unless(defined $GAPOK);
  if($BLASTIN==3) { ($nid_aln, $alnseq)= read_blast3fmt($inh,$iname); }  
  else { ($nid_aln, $alnseq)= read_blast0fmt($inh,$iname); $BLASTIN=1; }  ## if($BLASTIN==1)
  
} elsif($EXONRIN) {
  $GAPOK=1 unless(defined $GAPOK);
  ($nid_aln, $alnseq)= read_exonerate($inh,$iname);  

} elsif($AXTIN) { 
  #  $ALIGNER='a'; lastz --format=axt pair aligns
  $GAPOK=0 unless(defined $GAPOK); #?? NOT default
  ($nid_aln, $alnseq)= read_axt($inh,$iname);  

} else {  # msa input, rename %seq > %msa
  my($id,$fa,%multaln)=(0,"");
  print "#read_msa($iname)\n" if($debug);
  if($intop =~ /^>(\S+)/){ $id=$1; }
  while(<$inh>) { # read cds.msa mult align seqs
    if(/^>(\S+)/){ my $d=$1; $multaln{$id}=$fa if($id); $id=$d; $fa=""; } else{ chomp; $fa.=$_; } 
  }
  $multaln{$id}=$fa if($id); 
  $nid_aln=scalar(keys %multaln); $alnseq= \%multaln;
  print "#multaln nid_aln=$nid_aln\n" if($debug);
}

my($preqgene,$prdistmat)=(0,$debug > 1); # preqgene is obsolete for snpdist_pairs(), ?
#o: my $dopreqgene= ($otype eq 'eqgene')?1:0; #? drop
#o: ($preqgene,$prdistmat)=($dopreqgene,!$dopreqgene);
#o: $prdistmat=1 if($debug>0); #?

if(defined $GAPOK){ $BASEOK{'-'} = $GAPOK; } # used by snpdist_pairs(), change for ALIGNER ? : not for msa, yes for blastin/exonrin

my($nid_snpdist, $snpdist_vals)= ($nid_aln<1) ? (0,undef) : snpdist_pairs($alnseq,$preqgene,$prdistmat); # modify to use tree common branch evd

# output otype now valid here, $otype eq 'eqgene' or $otype eq 'vals' or what?
classify_pairs($nid_snpdist, $snpdist_vals, $nid_atree, $atreedist, $atreeval, $atreenodea);

#----------------------

=item classify_pairs
  
  combine snpdist, atree_dist, genex.nparalog to call alt/pars
  
  problems:
     npar=2 allows 3+ (ie true alts) due to relaxed cutoffs,
     need to sort pairs by best > worst paralogs and impose npar limit
     .. expand npar score in classify to weight these flags: pp10 > pp(\d+) .. reduces ispar (or +isalt)
     .. sort pairs by isparscore =? snpvar/100 (par high) - snp_flag (n/16?) + (1 - cb_pi/100)?
     parv.5050t1-2400t1 = .34 -  0/16 + .014 (1-.986) = 0.354
     parv.2400t1-2400t2 = .39 - .20 (10/50?) + .001 = 0.191
       pp10 score= .625 (10/16?) or .20 (10/50)? .. how much wt to flags?
       
  case: aweed tr2aacds_at18tairam/tmps4d
  pt=EVGm000628; env nparalog=0 noval=0 debug=2 $evigene/scripts/genes/altparsnpdist.pl $pt.{msa,tree}
  
  nparalog=0 : correct, note alt sn0:38.5,pp10= Altpp10,NOpar:s1spot,67:2,0:1
	AT2G25050t1	0	0	0	AT3G32400t1/sn1:33.8,0.0289,cb0:98.6,AT3G32400t2/sn1:32.0,0.0277,cb0:98.6
	AT3G32400t1	AT3G32400t2/sn0:38.5,0.00208pp10,cb0:99.9	0	0	AT2G25050t1/sn1:33.8,0.0289,cb0:98.6
	AT3G32400t2	AT3G32400t1/sn0:38.5,0.00208pp10,cb0:99.9	0	0	AT2G25050t1/sn1:32.0,0.0277,cb0:98.6
  
  nparalog=2 : genex val, sn1:38.5,pp10 < overridden by npar=2
	AT2G25050t1	0	0	0	AT3G32400t1/sn1:33.8,0.0289,cb0:98.6,AT3G32400t2/sn1:32.0,0.0277,cb0:98.6
	AT3G32400t1	0	0	0	AT2G25050t1/sn1:33.8,0.0289,cb0:98.6,AT3G32400t2/sn1:38.5,0.00208pp10,cb0:99.9
	AT3G32400t2	0	0	0	AT2G25050t1/sn1:32.0,0.0277,cb0:98.6,AT3G32400t1/sn1:38.5,0.00208pp10,cb0:99.9
     
=item class cMISS == no align (blastn)

Hs_g100132406t1 Hs_g400818t1utrorf      alt_i   0       0       0       unk_sn  0       0       0       0       unk_ta  100.0   alt_cp  0
Hs_g100132406t1 Hs_g728841t1utrorf      alt_i   0       0       0       unk_sn  0       0       0       0       unk_ta  100.0   alt_cp  0
 ^^^  cMISS should not be alt_i, new class noaln_i ?
 
 vvv  what are thse big,poor aligns ? NonamEVm000019.cds
      Name=neuroblastoma breakpoint family member NN : large family,  
Hs_g100132406t1 Hs_g100132406t2 par_i   30.4    323604  28843   par_sn  2.9     20.4    91.1    34      s3spot  0.00    86.71   unk_ta  100.0   alt_cp  0
Hs_g100132406t1 Hs_g100288142t1 par_i   31.7    523207  47552   par_sn  0.1     21.7    90.9    31      s3spot  0.00    86.41   unk_ta  100.0   alt_cp  0
Hs_g100132406t1 Hs_g100288142t2 par_i   31.7    535003  48748   par_sn  0.1     21.7    90.9    32      s3spot  0.00    86.37   unk_ta  100.0   alt_cp  0
Hs_g100132406t1 Hs_g101060226t1 par_i   31.6    354031  32704   par_sn  0.2     21.6    90.8    37      s3spot  0.00    86.51   unk_ta  100.0   alt_cp  0
ID____          JD____          Ispar   par_sc  Naln    Nsnp    S_par   S_idmax S_var   S_ident d3Codon S_peak  S_iCBIN S_MMF   B_par   B_ident C_par   C_cid

=cut


sub class_idmax { #UPD23JAN17
  my($idmax,$cna,$cnb)=@_; 
  # my($IDX_PAR,$IDX_ALT,$IDX2CN)=(SNP_IDMAX_ISPAR,SNP_IDMAX_ISALT,SNP_IDMAX_NPARCUT); # 55 best? idx2cn 85,35 == max,min, weed,hum differ on best
  my($IDX_ALT1C,$IDX_PAR2C)= ($idmax + 0.05, $idmax - 0.05); # test..

  my $cn=$cna; if($cnb>0){ $cn=($idmax < $IDX2CN)? _max($cna,$cnb) : _min($cna,$cnb); }  
  my ($cidp,$idxc)= ($idmax <= 0)? (cUNK,$idmax)
    : ($idmax>=$IDX_ALT)?(cALT,$idmax) #(ISALT_LIKE,$idmax) 
    : ($idmax<=$IDX_PAR)?(cPAR,$idmax) #(ISPAR_LIKE,$idmax)
    : ($cn >= NPARDUP)?(cPAR,$IDX_PAR2C) #(ISPAR_MAY,$IDX_PAR2C) 
    : (cALT,$IDX_ALT1C); #(ISALT_MAY,$IDX_ALT1C); 
  return (wantarray)? ($cidp,$cn,$idxc) : $cidp; 
}

# my($egids)= groupout_ispar($evgroupId, \@ids, \@jds, \%grvals); # fixme for 1 idi, prvals{jd}
sub groupout_ispar {  # UPD23JAN17 from apclass_idxdnacn.pl groupout_ispar()
  my($egid,$ida,$jda,$apvals)=@_;
  ##    $sim{$id}{$jd}= $idmax_tc; # $idmax; #?? 
  ##    $ispar{$id}{$jd}= ($ctcl=~/par/)?cPAR :($ctcl=~/alt/)?cALT: cUNK;      
  
  my($SGID)=(0); # this needs to be param?? global?
  my(@sgid,%sgidx,%sgroup,%gsim,%gisalt,%gpar);
  
  my %ids= map{ $_ => 1 } (@$ida, @$jda); # sort keys %$apvals;
  my @ids= sort keys %ids;
  
  # use Naln instead of genesize to get sl?  $apvals{$id}{$jd}{naln} ==  prval_Naln 

  my $dosortz= 1; # was $ngenex
  if($dosortz and ref($apvals)) { # resort by sizes, affects pubid alt/par calls
    if($ngenex) {
      @ids= sort { $genesize->{$b} <=> $genesize->{$a} or $a cmp $b } @ids;
    }
    my %simm; # ? sort by hi sim may put short alts at top, use clen?
    for my $id (@ids) { 
      my $sx=0; 
      # my $ilen= $genesize->{$id}||1;
      for my $jd (@ids) { if($id ne $jd){ 
        # my $jlen=$genesize->{$jd}||1;
        # my $alen= _min($ilen,$jlen);
        my $alen= $apvals->{$id}{$jd}{naln} || 1; 
        my $s = $apvals->{$id}{$jd}{idmax} || 1; $s= $s / 100; 
        my $sl= $alen*$s; # try sort by max ident bases
        $sx=$sl if($sl > $sx); 
        } 
      }
      $simm{$id}= $sx;
    }
    @ids= sort{ $simm{$b} <=> $simm{$a} or $a cmp $b } @ids;
  }
  
  # NOTE: id can have both alt and par pairs of same evgroup, sort above affects which is final call,
  #  .. can count these ambig. ids, no clear resolution, unless majority vote desired, vs above sort on max-ident
  
  my @idungr= @ids;
  while(@idungr > 0) { # .. group all
    my $id= shift @idungr; # remove now, starts new apgroup
    my @sgr=($id);
    for my $jd ( @idungr ) { 
      my $isap= $apvals->{$id}{$jd}{ispar} || cUNK;
      # my $isapr= $apvals->{$jd}{$id}{ispar} || cUNK;
      # if($isap ne $isapr) { } #?? fix here or not?
      if($isap <= cALT) { push @sgr,$jd; }  #?? is cUNK part of @sgr or no?
    }
    
    my $sgid= "p" . ++$SGID;  push @sgid,$sgid; 
    my @xid=@sgr; 
    my %xid= map{ $_,1 } @xid;
    my $ti=0; for my $xid (@xid) { ++$ti; $sgroup{$xid}= $sgid . "t$ti"; } # add altnum, using new sort order?
    @idungr= grep{ not $xid{$_} } @idungr;
  }
  
  my(%egid); 
  for my $id (@ids) { #(@$ida) 
   for my $jd (@ids) { # (@$jda)
    next if($id eq $jd or $id =~ /^ID/); # { $idp="EGapid"; }
    my $idp= $sgroup{$id}||"na";  
    my $jdp= $sgroup{$jd}||"na"; 
    #my $sim= $apvals->{$id}{$jd}{idmax};
    my $ispar= $apvals->{$id}{$jd}{ispar}; #?? is this bad
    my $palt=($ispar>=cPAR)?"par_e":($ispar<=cALT)?"alt_e":"unk_e";
    $egid{$id}{$jd}= "$egid$idp,$palt,$jdp"; #? dont need ,$sim
   }
  }
  return(\%egid); #?? need pair hash?
}  

sub classify_pairs {
  my($nid_snpdist, $snpdist_vals, $nid_atree, $atree_pairdist, $atree_commbranch, $atree_nodearray)=@_;

  # prvals flags: PRVALS_ENDOUT => 2  to put outside for id (@ids){} loop
  use constant { PRVALS_ENDOUT => 2, CODON3OUT => 1, PSIMnotPDIF => 1, SCALE_MMF => 1 };      
  
  my $OHDR=0;
  my($preqgene,$prvals)=(1,0); # always print eqgene table?? no, change
  if($otype eq 'eqgene') { $preqgene=1; $prvals=0; }
  elsif($otype eq 'vals' or $otype eq 'dist') { $preqgene=0; $prvals=1; } 
  
  my $nparclass= ($nparalog > 1.49) ? int(0.5+$nparalog) : ($nparalog>0) ? int(0.5+$nparalog) : 0;
  
  if($nid_snpdist < 1 and $nid_atree < 1) {
    return(0); #err
  }
  
  # if($nid_snpdist != $nid_atree) { .. } # ok?
  my %ids;
  if($nid_snpdist > 0) { map{ $ids{$_} |= 1 } keys %{$snpdist_vals};  }  
  ## atree_pairdist== \%cbdist {id}{jd}, \%cbval{lidn}, $rootc, $idn, $lidn
  if($nid_atree > 0) { map{ $ids{$_} |= 2 } keys %{$atree_pairdist};  }  

  my $CUTpars= 0.5 * $MINPAR_snpvar/100; # UPD22DEC23 testing
  
  my($commbr_npid,$commbr_npdist, $commbr_npispar) = commonBranch_of_nParalog($nparclass, $atree_commbranch);
  # cbofnpar : return($cbid, $cbdist); # %$commbr_npispar == clid of cbpar? ret \@cbpar, \@cbalt ??
  my $cbndist= sprintf"%.3g",$commbr_npdist;
  print "#dbg: commonBranch_of_nParalog nparc=$nparclass, commbr_npid=$commbr_npid commbr_npdist=$cbndist\n" if($debug);
  
  # @ijvals or %ijvals ? as with $snpdist_vals->{$id}{$jd}
  my (@prvals_hdr, %prvals); # was inside id (@ids)
  my @ids= sort keys %ids; my $i=0;
  for my $id (@ids) {
    my @alt=(); my @par=(); 
    my (%par_score,%par_val,%par_cbnpar);
    # %prvals=() if(PRVALS_ENDOUT == 1);
    for my $jd (@ids) {  
      next if($jd eq $id);

      my $ispar_sd= $snpdist_vals->{$id}{$jd}{'ispar'} ; # cPAR|cALT|cUNK now
      my $fl_sd   = $snpdist_vals->{$id}{$jd}{'pflag'} || ""; #  $pflag="pp$spoor"
         #^^ upd YESpar = bit 16 is opposite of NOpar = bit 8
      my $snpvar  = $snpdist_vals->{$id}{$jd}{'vD2sn'} || 0;
      my $pdif_sd = $snpdist_vals->{$id}{$jd}{'pDsn'} || 0;
      my $idmax_sd= $snpdist_vals->{$id}{$jd}{'idmax'} || -1;
      my $notes_sd= $snpdist_vals->{$id}{$jd}{'Note'} || "";
     # for my $i (0..$#row) { $snpvals{$idi}{$idj}{$snpd_keys[$i]}= $row[$i]; }
      # snp_distke @hd=qw(Nc Nd pDsn aD2sn vD2sn mD2sn D2span Note); # drop: Ne aEsn vEsn mEsn Espan
      my($peaks_sd)= $notes_sd =~ m/(s.spot)/?$1:0;
      
      my($dCodon, $iCBIN, $MMF)= # UPD23JAN01
        map{ $snpdist_vals->{$id}{$jd}{$_} || 0; } qw( dCodon iCBIN MMF);

      my($cni, $cnj)= ($nparclass,0);
      if($ngenex){ $cni= $genexcopy->{$id}|| $nparclass; $cnj= $genexcopy->{$jd}||0; }
      my($ispar_tc,$npar_tc,$idmax_tc)= class_idmax($idmax_sd, $cni, $cnj); #UPD23JAN17
      
      # NOW have global @atreedist_keys = value keys in atree_pairdist->{id}{jd}{@keys}
      # @atreedist_keys= qw( clid clidp  dist ispar isparval ); # for atree_pairdist->{id}{jd}{@keys}

      my($ispar_ta, $ispar_taval,$clid, $clidp, $cndif, )= (cUNK, (0) x 9);
      if($nid_atree>1) {
        ($ispar_ta, $ispar_taval,$clid, $clidp, $cndif, )=
          map{ $atree_pairdist->{$id}{$jd}{$_}||0 } qw( ispar isparval clid  clidp dist); # @atreedist_keys
      } 
      
      my $ispar_cbnpar= cUNK; # == cMISS
      if($commbr_npid) {
        $ispar_cbnpar= $commbr_npispar->{$clid}; #?? replace below npar>nparalog fixup
        $par_cbnpar{$jd}= $ispar_cbnpar; #?
      }
      
      my $MAXD=1; #fixme: if($otype eq 'sim' and $maxdif > 1){ $MAXD=$maxdif; } #what?
      my $cident= 100*($MAXD - $cndif);
      # my $ispar_ta=-1; #  $ispar_ta=($cident <= $MINALT_tree)?1:0;
    
      my $cidentp= sprintf"%.1f", $cident;
      my $pval="";  #NOTE: pval cant have ',' as @alt,@par are ID,ID sep vals
      if($PVALfmt==1){ $pval=""; }
      elsif($PVALfmt==0) { # long
      $pval= "/sn$ispar_sd:$snpvar:$pdif_sd$fl_sd/cb$ispar_ta:$cidentp"; #? too much pair vals?
      $pval.="/tc$ispar_tc:".int(0.5+$idmax_tc); #? if($ispar_tc!=cUNK); #UPD23JAN17
      # $pval.="/cp$ispar_cbnpar:$clid" if($ispar_cbnpar>=0); #UPD23JAN17
      # add idmax_sd to pdif_sd ?
      }
      else { $pval="/sn$ispar_sd/cb$ispar_ta/tc$ispar_tc"; } #short
      #else { $pval="/sn$ispar_sd/cb$ispar_ta/cp$ispar_cbnpar"; } #short
      
      # add combo score to sort pairs, use nparalog=2 to separate top 1 (par) from other 2 (alts)
      my($fl_sdv)= ($fl_sd =~ m/pp(\d+)/)?$1:0;
      my($fl_asense)= ($fl_sd =~ m/asense/)?-1:0; ## UPD23FEB20, now also yespar &16 
      my $fl_yespar=0; if($fl_sdv & 16) { $fl_sdv -= 16; $fl_yespar=16; } # upd YESpar:s3spot flag AND idmax.ispar??
      #?? add asense to peaks_sd string for output table ?? or new column?
      if($fl_asense < 0) { $peaks_sd = "-sense,$peaks_sd"; } # -sense is tr2cds flag, use that?
        
      my $ispar_score= $cndif + $snpvar/100 + $fl_yespar/50 - $fl_sdv/50; # change? see below UPD22DEC23

    #UPD22DEC29: modify ispar class and par_score here?
    # .. based on strong par evd (low idmax<=35, s3spot) or strong alt evd (hi idmax>=75, s[01]spot)
    # BUt ispar_sd already accounts for these, add value there? ispar_sd >= par_certain or <= alt_certain ?
    # .. see altpar_snpdist_max_idspan.txt ref result tables
      
      my $ispar= cUNK; # unk .. fixme no-align => alt bad call
      # test nparalog 3 levels?  npar==1, npar>1, npar==0/noinfo
      # distinguish == 0/isalt vs <0/noinfo ?
      # UPD23JAN17: ispar_tc add, ?drop ispar_cbnpar, revise to skip if cUNK/cMISS

if(UPD23JAN17) { # bug here??
      my @val_par= grep{ $_ >= cPAR } ($ispar_tc, $ispar_sd, $ispar_ta);
      my @val_alt= grep{ $_ <= cALT } ($ispar_tc, $ispar_sd, $ispar_ta);

      # my @val_par= grep { int($_) >=  1 } ($ispar_tc, $ispar_sd, $ispar_ta);
      # my @val_alt= grep { int($_) <= -1 } ($ispar_tc, $ispar_sd, $ispar_ta);
      # my @val_unk= grep { $_ == cUNK } ($ispar_tc, $ispar_sd, $ispar_ta);

      # if(@val_unk == 3 or (@val_par == 0 and @val_alt == 0)) { # miss here ??
      #  $ispar= cUNK;
      #} els
      if(@val_par > 0 and @val_alt == 0) {
        my $nc=@val_par; $ispar= cPAR; # ($nc>1)?ISALT_CERT:ISPAR_LIKE;
      } elsif(@val_par==0 and @val_alt > 0) {
        my $nc=@val_alt; $ispar= cALT; # ($nc>1)?ISALT_CERT:ISALT_LIKE;
        
      } elsif(@val_par == 0 and @val_alt == 0) { # miss here ??
        $ispar= cUNK;
        
      } else {
        if($ispar_tc >= cPAR) { $ispar= cPAR; } #ISPAR_MAY;   # trust?? not always, asense changes
        elsif($fl_asense < 0 and $ispar_sd >= cPAR) {$ispar= cPAR; }
        elsif($ispar_tc <= cALT) { $ispar=cALT; } #ISALT_MAY;   #  not always, asense changes
        elsif(@val_par>@val_alt) {  $ispar= cPAR; } #ISPAR_MAY;  
        elsif(@val_par<@val_alt) {  $ispar= cALT; } #ISALT_MAY;  
        else { # cUNK? or what
#           $ispar= (abs($val_par[0]) > abs($val_alt[0])) ? ISPAR_MAY 
#                 : (abs($val_par[0]) < abs($val_alt[0])) ? ISALT_MAY : cUNK; # or cALT? unlikely
          $ispar= (abs($val_par[0]) > abs($val_alt[0])) ? cPAR 
                : (abs($val_par[0]) < abs($val_alt[0])) ? cALT : cUNK; # or cALT? unlikely
        } 
      }
      
} else {      
         if($ispar_sd >= cPAR and $ispar_ta >= cPAR)  { if($nparclass==1) { $ispar= ISPAR_MAY; } else { $ispar= ISPAR_CERT;  } }   
      elsif($ispar_sd < cPAR and $ispar_ta < cPAR) { if($nparclass==1) { $ispar= ISALT_CERT; } else { $ispar= ISALT_MAY;  } } 
      elsif($ispar_sd >= cPAR and $ispar_ta < cPAR) { 
        if($ispar_cbnpar>0) { $ispar= ISPAR_LIKE; } else { 
          if($nparclass==1) { $ispar= ISPAR_MAY; } else { $ispar= ISPAR_LIKE;  }  #? trust ispar_sd ?
          }
        }
      elsif($ispar_sd < cPAR and $ispar_ta >= cPAR)  { 
        if($nparclass==1) { $ispar= ISALT_MAY; }   
        else { $ispar= ($fl_yespar) ? ISPAR_LIKE : ($snpvar < $MINPAR_snpvar or $fl_sdv > 0)? ISALT_MAY: ISPAR_MAY;  } # rely on snp_dist not traln
        } # dont trust ispar_ta?
}
      
if(UPD22DEC23) {
      #try1: $ispar_score= $snpvar/100 - $fl_sdv/50; # drop $cndif , use below w/ alt2par, score > 0.10 ?  MINPAR_snpvar/100
      # try1 cuts pars, not useful; 
      # try2 try diff cancel score: alts == /sn0:0:0pp2/cb-1:100.0
      # try2: /sn$ispar_sd:$snpvar:$pdif_sd$fl_sd/cb$ispar_ta:$cident
      # .. no go, for npar>=2, move alt2par using these various snpdist+treedist scores doesn't help; all worse than nomove
      # .. but    try npar==1, move par2alt, using? inverse of this or other snp+tree scores
      if($nparclass==0) {  
        $ispar_score=$snpvar; $CUTpars= 100; 
      } elsif($nparclass >= 2) {
        $CUTpars= 0.5 * $MINPAR_snpvar; # was 0.5 * $MINPAR_snpvar/100; # change
        $ispar_score=$snpvar;
        if($fl_yespar) { 
          $ispar_score += 10;  # superceed others?
        } else {
          $ispar_score=0 if(($snpvar < 2) or ($fl_sdv & 2+8)); # ($fl_sdv & 2+8) == SNPLOW | NOPAR
          $ispar_score=0 if($cident > 99.9);
        }
      } elsif($nparclass==1) {
        #* need diff CUTpars and score for nparclass==1 see CUT1pars
        $CUTpars= 2 * $MAXMAXALT_snpvar; #? boost
        $ispar_score=$snpvar;
        ## $MIN_MINALT_tree=$ENV{pminminalt}|| 90.0; # force paralog at lower idents, dunno what default, 95..98 
        if($fl_yespar or $cident < $MIN_MINALT_tree) { # always accept as par?
          $ispar_score = 1+$CUTpars; # += 10; # if have these par flags, boost this JD score
        } else {
          $ispar_score=0 if(($snpvar < 2) or ($fl_sdv & 2+8)); 
          $ispar_score=0 if($cident > 97.9); # LOWER cident here
        }
      }
}
      $par_score{$jd}= $ispar_score;
      $par_val{$jd}= $pval;
      if($ispar>=cPAR) { push @par, $jd; }  else {  push @alt, $jd; } # separate id, pval 

=item prvals

      my $ispar_sd= $snpdist_vals->{$id}{$jd}{'ispar'};
      my $fl_sd   = $snpdist_vals->{$id}{$jd}{'pflag'} || ""; #  $pflag="pp$spoor"
         #^^ upd YESpar = bit 16 is opposite of NOpar = bit 8
      my $snpvar  = $snpdist_vals->{$id}{$jd}{'vD2sn'} || 0;
      my $pdif_sd = $snpdist_vals->{$id}{$jd}{'pDsn'} || 0;
      my $idmax_sd= $snpdist_vals->{$id}{$jd}{'idmax'} || -1; #? -1 is missing val
      my($fl_sdv)= ($fl_sd =~ m/pp(\d+)/)?$1:0;
      my $fl_yespar=0; if($fl_sdv & 16) { $fl_sdv -= 16; $fl_yespar=16; } # upd YESpar:s3spot flag
      peaks_sd
      
      $pval= "/sn$ispar_sd:$snpvar:$pdif_sd$fl_sd/cb$ispar_ta:$cidentp"; #? too much pair vals?
      $pval.="/cp$ispar_cbnpar:$clid" if($ispar_cbnpar>=0);

  add to snpdist_vals:  Nd Nc counts,
    Nc3, Nc2, Nc1 : codon snp counts, or p(N3/Nc) 
    block counts of other pp: count same/diff blocks, and sum(blocksize-1)/Nbases

  * Change pdif_sd to pident_sd = (100 - 100*pdif_sd) == Seq Similarity, Feat1 of IsoSVM06 altparclass paper
  * FIXME: move below cbmovepar2alt/alt2par to capture those ispar changes !
  ? drop C_cid, S_fnot S_fyes
  
  *** BUG pid == 0, cant be pdif==100, change evg altpar code...
  *** change altparsnd.pl vals output: Sn_ident instead of pdif, 100* Sn iCBIN & MFF, drop fnot/fyes

grep -v '#' cacao19cr_cdschr1adupx7t.vals | cut -f3,4,5,6,8,9,10,11,13,14,16 | perl -ne 'if(/B_ident/){ print if(1>$hdr++); } else { print; }' | env zero=1 header=1  $evigene/scripts/genoasm/cds_meanvar.pl
cacao19cr_cdschr1adupx7t  stats for nt=2436
Item    Median  Mean    SEM     Nitem   StDev   Range
par_sco 37.20   29.25   0.33    2436    16.15   0-58.6
  Naln   768    1018.25 16.40   2436    809.53  0-6660
  Nsnp    39    106.89  3.54    2436    174.73  0-1196
S_idmax 15.50   31.20   0.68    2436    33.43   0-100
 S_var  27.40   22.38   0.24    2436    11.81   0-70.1
S_ident 94.70   85.71   0.39    2436    19.40   0-100.0
d3Codon   34    37.03   0.28    2436    13.76   0-100
S_iCBIN 1.54    11.94   0.54    2436    26.90   0-100.00
 S_MMF  90.98   83.46   0.38    2436    18.86   0-99.98
B_ident 97.40   95.06   0.15    2436    7.35    59.8-100.0

cacao19cr_cdschr1auniq7t.vals
 stats for nt=1794
Item    Median  Mean    SEM     Nitem   StDev   Range
par_sco    0    1.80    0.20    1794    8.37    0-41
  Naln   1272   1663.44 27.25   1794    1154.22 210-9363
  Nsnp     0    9.85    0.85    1794    36.00   0-374
S_idmax  100    92.92   0.48    1794    20.35   1.2-100
 S_var     0    1.63    0.14    1794    5.97    0-39.5
S_ident 100.00  99.15   0.07    1794    2.87    55.5-100.0
d3Codon   33    33.54   0.26    1794    10.91   0-100
S_iCBIN 100.00  72.92   0.98    1794    41.48   0.20-100.00
 S_MMF  99.90   98.77   0.09    1794    3.83    50.60-99.99
B_ident 100.00  99.60   0.04    1794    1.51    77.8-100.0
  
=cut

   
      if($prvals) { # output all id,jd vals plus ispar class
      
        # my %parcode=(   # this failed .. why? got "un$i"_sn for parc('sn',$ispar_sd), but  parc('i',$ispar) worked     
        #   -1 => 'alt', cALT => 'alt', ISALT_CERT => 'alt', ISALT_LIKE => 'alt', ISALT_MAY => 'alt',
        #   1 => 'par', cPAR => 'par',  ISPAR_CERT => 'par', ISPAR_LIKE => 'par', ISPAR_MAY => 'par',
        #   0 => 'unk', cUNK => 'unk' ); # ugh, code change alt <= -1, par >=1, unk==0
        # sub parc{ my($t,$i)=@_; my $c=$parcode{$i}||"un$i"; return $c."_$t"; }
        
        sub parc{ my($t,$i)=@_; my $c= ($i >= cPAR)?'par' : ($i <= cALT)?'alt' : 'unk'; return $c."_$t"; }
        
        # add sizes Nc Nd
        my($Nc_sn,$Nd_sn)= map{ $snpdist_vals->{$id}{$jd}{$_} || 0; } qw(Nc Nd);

        my $dcLab="dCodon";
        if(CODON3OUT) {  $dcLab="d3Codon";  my @dc=split",",$dCodon; $dCodon= $dc[-1] || 0; } #? 0 or 33 for missing/nosnp ?
        
        my $pdLab="S_pdif"; my $pdval_sd= $pdif_sd; #= ($nc<1)?0: sprintf("%.3g",$nd/$nc); 
        #if(PSIMnotPDIF){ $pdLab="S_psim"; $pdval_sd= ($Nc_sn<1)?0: sprintf("%.3f",100 - 100 * $Nd_sn/$Nc_sn); }
        # S_psim > S_ident ?? have B_ident, no other
        if(PSIMnotPDIF){ $pdLab="S_ident"; $pdval_sd= ($Nc_sn<1)?0: sprintf("%.1f",100 - 100 * $Nd_sn/$Nc_sn); }
        #do.below: if(SCALE_MMF) { ($iCBIN, $MMF)= map{ sprintf "%.1f",100*$_; } ($iCBIN, $MMF); }
        my $idmax_sdz= ($idmax_sd < 0)?0:$idmax_sd; # -1 missing val no good here

use constant { prval_Ispar => 2, prval_Naln => 4, prval_Nsnp => 5, prval_idmax => 7, }; # change @prvals to keyed hash?        
        @prvals_hdr= ( qw(ID____ JD____ Ispar par_score Naln Nsnp S_par S_idmax S_var ),
           $pdLab,$dcLab, qw(S_peak S_iCBIN S_MMF B_par B_ident  D_par D_idx
           )); # drop fnot,fyes; C_par C_cid for D_ idmax class; maybe drop par_score? less useful
        
        #d my $api= ($ispar==0) ? 'unk_i' : ($ispar >= 1)?'par_i' : ($ispar <= -1) ? 'alt_i' : 'unc_i';  #DEBUG  parc('i',$ispar)  
        my @prvals= ($id, $jd, parc('i',$ispar), $ispar_score, $Nc_sn, $Nd_sn, 
          parc('sn',$ispar_sd), $idmax_sdz, $snpvar, $pdval_sd,  $dCodon, $peaks_sd, $iCBIN, $MMF,
          # drop: $fl_sdv, $fl_yespar,
          parc('ta',$ispar_ta), $cidentp, 
          parc('tc',$ispar_tc), $idmax_tc, #UPD23JAN17 tc$ispar_tc:".int(0.5+$idmax_tc); 
          # parc('cp',$ispar_cbnpar), $clid,
          ); # ** change C_par,C_cid to idmax_ispar
        
        if(PRVALS_ENDOUT) {
          $prvals{$id}{$jd}= \@prvals; # change to prvals{id}{jd} ?
        } else {
          print join("\t",@prvals_hdr)."\n"  if(1 > $OHDR++);
          print join("\t",@prvals)."\n";
        }  
        # print join("\t",qw(ID____ JD____ Ispar par_score Naln Nsnp S_par S_idmax S_var ),
        #    $pdLab, qw(S_peak S_fnot S_fyes), $dcLab, # maybe drop fnot,fyes
        #    qw(S_iCBIN S_MMF B_par B_ident C_par C_cid))."\n" if(1 > $OHDR++); 
        # print join("\t",$id, $jd, parc('i',$ispar), $ispar_score, 
        #   $Nc_sn,$Nd_sn, 
        #   parc('sn',$ispar_sd), $idmax_sd, $snpvar, $pdval_sd, $peaks_sd, $fl_sdv, $fl_yespar,
        #   $dCodon, $iCBIN, $MMF,
        #   parc('ta',$ispar_ta), $cidentp, 
        #   parc('cp',$ispar_cbnpar), $clid)."\n"; 
      }
      
     }

    my $npar= @par; my $nalt= @alt; my $npair= $npar+$nalt;
    # my $npar1c= _minNotZero($npair,$nparclass);  $npar1c--;
    my $npar1c=($nparclass>0 and $nparclass-1 < $npair)? $nparclass-1 : $npair;
   
    # ** not right, need tree common_branch cut point ***
    # now pairwise:  
    #   par1t1 > alt=alt2t2  par=alt2t1 .. mistake, split at tree cb1 instead 
    #   alt2t1 > alt=alt2t2  par=par1t1
    #   alt2t2 > alt=alt2t1  par=par1t1
    
    ## classify_pairs opts: now at top
    ## make this default: $ENV{moveparcb} seems to work ok now; turnoff= $ENV{nomoveparcb}
    # my $DOmoveparcb   = $ENV{nomoveparcb} ? 0 : 1; #was $ENV{moveparcb}
    # my $DOmovepar2alt = $ENV{nomovepar2alt} ? 0 : not $DOmoveparcb; # old way .. drop?
    # my $DOmovealt2par = $ENV{movealt2par} ? not $DOmoveparcb : 0; # drop?
    
    #ab: use constant UPD22DEC23 => 1; # revise move alt2par, par2alt based on tests. 
    #   for nparclass>1, only alt2par-cb, for nparclass==1, only par2alt-cb
if(UPD22DEC23) {

    #UPD22DEC29: new kind of par_score{id,jd} needs both par2alt and alt2par actions
    # .. based on strong par evd (low idmax<=35, s3spot) or strong alt evd (hi idmax>=75, s[01]spot)
    # .. see altpar_snpdist_max_idspan.txt ref result tables
    # * OR move this fix above to ispar tests..
    
    # use option $DOmoveparcb only? NO, make cbmovealt2par default off, sep opt for cbmovepar2alt
    # if($nparclass == 0) # dont move, no info
    my $cbmovepar2alt = ($DOcbmovepar2alt and $nparclass==1 and $commbr_npid and $npar > $npar1c);
    my $cbmovealt2par = ($DOcbmovealt2par and $nparclass>=2 and $commbr_npid and $npar < $npar1c);
    
    #TRY: cbmovealt2par = 1 for $DOcbmovepar2alt and $nparclass==1 and npar==0 but YESpar > 0, 
    #     nmove= sum(YESpar) in @alt
    #  eg: g2999t2	alt=g3002t1/sn0:32.9:0.161pp16/cb0:90.9/cp0:1,
    
    if($cbmovepar2alt) { # nmove= npar > npar1c
      my $nmove= $npar - $npar1c;
      #ok1: my @salt= grep { $par_cbnpar{$_} == 0 } @par;
      ## OR test this..
      # my $CUT1pars= $CUTpars; #fixed above $MAXMAXALT_snpvar; # CUTpars = 0.5 * $MINPAR_snpvar; # change
      my @salt= grep { $par_score{$_} < $CUTpars } @par;
      if(1 and @salt > $nmove){ @salt= sort{ $par_score{$a} <=> $par_score{$b} } @salt; @salt= splice(@salt,0,$nmove); }
      my $nmoved= @salt;
      print "#dbg: altpar_move par2alt nmove=$nmoved/$nmove, npar=$npar, nalt=$nalt, npar1c=$npar1c salt=@salt\n" if($debug);
      if($nmoved > 0) {
        my %salt=map{ $_ => 1} @salt; my @spar= grep{ not $salt{$_} } @par;
        @par=@spar; push @alt, @salt; $npar=@par; $nalt=@alt;      
      }
    
    } elsif($cbmovealt2par) {  # nmove = npar < npar1c
      # not good for human 3t test, many true alts moved from alt to par, 
      # conflict cb-val(high) w/ snpdist vals(low) .. use par_score{} to cancel move?
      # eg: 2t (nomove): g107282092t6	alts=g107282092t3/sn0:0:0pp2/cb-1:100.0/cp1:4,g107282092t4/sn0:0:0pp2/cb-1:100.0/cp1:4
      #     3t (this)  : g107282092t6	alts=0	 pars=g124907841t1/sn1:28.1:0.48/cb1:90.7/cp1:1,..g107282092t3/sn0:0:0pp2/cb-1:100.0/cp1:4,..
      # 3tdbg: altpar_move alt2par nmove=2/2, npar=4, nalt=2, npar1c=6 spar=g107282092t3 g107282092t4
      #   chr.genex sez nparalog=9, ref actual npar=5 + nalt=3
      #.. this move alt2par worked only for no true alts, pars misclassed as alts
      #..try par_score filter? no good, all moves blocked.. lower CUTpars? not useful, slightly worse than no move alt2par
      
      my $nmove= $npar1c - $npar;
      #o1: my @spar= grep { $par_cbnpar{$_} > 0 or $par_cbnpar{$_} < 0 } @alt;
      #o2: my @spar= grep { $par_score{$_} >= $CUTpars and ($par_cbnpar{$_} > 0 or $par_cbnpar{$_} < 0) } @alt;
      #test w/o cb npar.. not worse than other tests w/ above
      
      #UPD: sort by >par_score, limit? to <= nmove, not >nmove ?? this may be more good than bad, 
      #  or a wash: nomove2t == move7t for refbad and nmoved > nmove, mostly nn alts are moved2par, some from altof.paralog
      # eg: #dbg: altpar_move alt2par nmove=2/1, npar=0, nalt=2, npar1c=1 spar=g720t1 g720t2
      #g721t1	0	0	g720t1/sn0:5.4:0.00255/cb1:99.9/cp1:1,g720t2/sn0:5.3:0.00248/cb1:99.9/cp1:1
      # ^^ limit @spar to nmove has small good effect, ie reduces false alt2par more than true alt2par loss
      
      my  @spar= grep { $par_score{$_} >= $CUTpars } @alt;
      if(1 and @spar > $nmove){ @spar= sort{ $par_score{$b} <=> $par_score{$a} } @spar; @spar= splice(@spar,0,$nmove); }
      my $nmoved= @spar;
      print "#dbg: altpar_move alt2par nmove=$nmoved/$nmove, npar=$npar, nalt=$nalt, npar1c=$npar1c spar=@spar\n" if($debug);
      if($nmoved > 0) {
        my %spar=map{ $_ => 1} @spar; my @salt= grep{ not $spar{$_} } @alt;
        push @par, @spar; @alt= @salt; $npar=@par; $nalt=@alt;
      }
    
    }
    
} else {
    if($DOmoveparcb and $commbr_npid and $npar > $npar1c) {
    
      #PROBLEM: this is bad for cacao (ncbi19genes), works for arath .. likely quality/quantity of 'true' alts is cause
      #Tests w/ human, cacao, arath? say turn off move par2alt when nparalog >= 2, on when nparalog=1
      # for human, npar>=2, nomove, ap-call error is ~10% for both alt-calls and par-calls, 150/560 groups, 3000/30000 ntr
      # for human, npar==1, domove, ap-call err is <1% (alts only, no pars), 12/860 groups, 130/17700 ntr of subsample
      # .. havent tested any alt2par moves here.
      
      #FIXME: cases of many true par moved to alt here, due to bad nparclass value.. 
      # .. need to weight $ispar_score vs nparclass, use par_score{jd} ? par_val{jd}
      ## when nparclass is too low for par scores, this forces bad par2alt moves:
      ## need way to reset/boost nparclass from par_score/par_val,
      ## see also tree_altpar_mustcut, snpdist:YESpar:s3spot
      # my($commbr_npid,$commbr_npdist, $commbr_npispar) = commonBranch_of_nParalog($nparclass, $atree_commbranch);
      
      #?? add move alt2par here?
      my $nmove= $npar - $npar1c;
      my @salt= grep { $par_cbnpar{$_} == 0 } @par; #?? change == 0 to == cALT -1
      my @spar= grep { $par_cbnpar{$_} > 0 or $par_cbnpar{$_} < 0 } @par; #? change to != cALT
      my $nmoved= @salt;
      # $commbr_npid,$commbr_npdist
      print "#dbg: altpar_move par2alt nmove=$nmoved/$nmove, npar=$npar, nalt=$nalt, npar1c=$npar1c salt=@salt\n" if($debug);
      @par=@spar; push @alt, @salt; $npar=@par; $nalt=@alt;
      $npar1c=$npar; # block below test
    }
    
    if($nparclass == 0) { # dont move, no info
    
    } elsif($npar > $npar1c and $DOmovepar2alt) { #  move @par to @alt
      my $nmove= $npar - $npar1c;
      my @spar= sort{ $par_score{$a} <=> $par_score{$b} or $a cmp $b } @par;
      my @salt= splice(@spar,0,$nmove);
      print "#dbg: altpar_move par2alt nmove=$nmove, npar=$npar, nalt=$nalt, npar1c=$npar1c salt=@salt\n" if($debug);
      @par=@spar; push @alt, @salt; $npar=@par; $nalt=@alt;
      
    } elsif($npar < $npar1c and $DOmovealt2par) {  # move @alt to @par ?? trickier decision, needs testing, opt?
      my $nmove= $npar1c - $npar;
      my @salt= sort{ $par_score{$b} <=> $par_score{$a} or $a cmp $b } @alt;
      my @spar= splice(@salt,0,$nmove);
      print "#dbg: altpar_move alt2par nmove=$nmove, npar=$npar, nalt=$nalt, npar1c=$npar1c spar=@spar\n" if($debug);
      push @par, @spar;  @alt= @salt; $npar=@par; $nalt=@alt;
    }
} # not UPD22DEC23
    
    ##UPD23JAN17: insert here?  groupout() reclass pubids, use evgroupId, add alt/par nums
    ## need to move this outside id (@id) loop to group all ids for evgId
    
    if(PRVALS_ENDOUT == 1 and UPD23JAN17 and %prvals) { #MOVED below
      #  @prvals_hdr= ( qw(ID____ JD____ Ispar par_score Naln Nsnp S_par S_idmax S_var ),
      #     $pdLab,$dcLab, qw(S_peak S_iCBIN S_MMF B_par B_ident  D_par D_idx
      #     )); # drop fnot,fyes; C_par C_cid for D_ idmax class; maybe drop par_score? less useful

      my %grvals;
      my @ids=($id); # FIXME: not good, all @ids of id-loop needed for evgid
      my @jds=(@alt,@par);
      for my $jd (@alt) { $grvals{$id}{$jd}{ispar}= cALT; } # FIXME cUNK in @alt set
      for my $jd (@par) { $grvals{$id}{$jd}{ispar}= cPAR; }
      for my $jd (@jds) { 
        ##$grvals{$id}{$jd}{ispar}= $prvals{$jd}->[prval_Ispar]; #?? ispar or reclass @alt,@par ??
        $grvals{$id}{$jd}{idmax}= $prvals{$id}{$jd}->[ prval_idmax ]; # fixme
        $grvals{$id}{$jd}{naln}= $prvals{$id}{$jd}->[ prval_Naln ]; # fixme
          # use Naln instead of genesize to get sl?
      }
      my($egids)= groupout_ispar($evgroupId, \@ids, \@jds, \%grvals); # fixme for 1 idi, prvals{jd}
      push @prvals_hdr, "EvgroupID" if(1 > $OHDR); # or what
      for my $jd (@jds) { my $ed= $egids->{$id}{$jd}||"na"; my $jvals= $prvals{$id}{$jd} or next; push @$jvals, $ed; }
    }
    
    if($prvals) { # do above.. in loop @id .. @jd
      if(PRVALS_ENDOUT == 2) { # this is for cbmove changes, but dont change unk_i
        for my $jd (@alt) {
          my $jvals= $prvals{$id}{$jd} or next; 
          my $api=$jvals->[2];
          $jvals->[2]= "alt_i" unless($api eq "unk_i");  #? NO, cUNK here, want that
          }
        for my $jd (@par) {
          my $jvals= $prvals{$id}{$jd} or next; 
          $jvals->[2]= "par_i";  #?
          }
        
      } elsif(PRVALS_ENDOUT == 1) {
      print join("\t",@prvals_hdr)."\n"  if(1 > $OHDR++);
      for my $jd (@alt) {
        my $jvals= $prvals{$id}{$jd} or next; $jvals->[2]= "alt_i"; print join("\t",@$jvals)."\n";
        }
      for my $jd (@par) {
        my $jvals= $prvals{$id}{$jd} or next; $jvals->[2]= "par_i"; print join("\t",@$jvals)."\n";
        }
      }
    } else { # if($preqgene) 
      my $oid=$id; #?? or $lid
      my $alts=(@alt<1)?"0":join(",", map{ my $v=$par_val{$_}||""; $_.$v; } @alt);
      my $pars=(@par<1)?"0":join(",", map{ my $v=$par_val{$_}||""; $_.$v; } @par);
      print "#".join("\t","PublicID","OID","AltIDs","Locus","Mapqual","ParalogIDs")."\n" if($i == 0);  
      print join("\t",$id,$oid,$alts,0,0,$pars)."\n";
    }
    $i++;
    
  }
  
  # move here? all of PRVALS_ENDOUT w/ grouput egids
  #       my($egids)= groupout_ispar($evgroupId, \@ids, \@jds, \%grvals); # fixme for 1 idi, prvals{jd}

  if(PRVALS_ENDOUT == 2 and $prvals) { # UPD23JAN17

    ##UPD23JAN17: insert here?  groupout() reclass pubids, use evgroupId, add alt/par nums
    ## need to move this outside id (@id) loop to group all ids for evgId
    if(UPD23JAN17) {
      #  @prvals_hdr= ( qw(ID____ JD____ Ispar par_score Naln Nsnp S_par S_idmax S_var ),
      #     $pdLab,$dcLab, qw(S_peak S_iCBIN S_MMF B_par B_ident  D_par D_idx
      #     )); # drop fnot,fyes; C_par C_cid for D_ idmax class; maybe drop par_score? less useful

      my %grvals;
      my @idi=@ids; #($id); # FIXME: not good, all @ids of id-loop needed for evgid
      my @jds=@ids; # (@alt,@par);
      # for my $jd (@alt) { $grvals{$id}{$jd}{ispar}= cALT; }
      # for my $jd (@par) { $grvals{$id}{$jd}{ispar}= cPAR; }
      for my $id (@idi) { for my $jd (@jds) { next if($jd eq $id);
        my $sip= $prvals{$id}{$jd}->[ prval_Ispar ];
        my $cip= ($sip =~ m/par/)? cPAR : ($sip =~m/alt/)? cALT : ($sip=~/unk/)? cUNK: 0; #??
        $grvals{$id}{$jd}{ispar}= $cip; #?? ispar or reclass @alt,@par ??
        $grvals{$id}{$jd}{idmax}= $prvals{$id}{$jd}->[ prval_idmax ];  
        $grvals{$id}{$jd}{naln}= $prvals{$id}{$jd}->[ prval_Naln ];  
       } 
      }
      my($egids)= groupout_ispar($evgroupId, \@idi, \@jds, \%grvals); # fixme for 1 idi, prvals{jd}
      push @prvals_hdr, "EvgroupID" if(1 > $OHDR); # or what
      for my $id (@idi) { for my $jd (@jds) { 
        my $ed= $egids->{$id}{$jd}||"na"; my $jvals= $prvals{$id}{$jd} or next; push @$jvals, $ed; 
        }
      }
    }
  
    print join("\t",@prvals_hdr)."\n"  if(1 > $OHDR++);
    for my $id (@ids) {
      for my $jd (@ids) { next if($id eq $jd);
        my $jvals= $prvals{$id}{$jd} or next; 
        print join("\t",@$jvals)."\n";
        }
    }
    
  }
  
}

sub snpdist_pairs { 
  my($alnseq,$preqgene,$prdistmat)=@_;
  my @ids=sort keys %$alnseq; # fixme for lastz --notrivial needs id/jd swap
  my $nids=@ids; 
  my($hdr,%snpvals)=(0);
  #UPD23FEB20: $AXTIN=1; $ALIGNER='a'; lastz --format=axt pair aligns
  my $alnIsPairs= ($AXTIN||$BLASTIN||$EXONRIN)?1:0;#  $alnseq{id}{jd} vs alnseq{id}
  $alnIsPairs=3 if($BLASTIN==3);
  # EXONRIN need alnIsPairX : $alnseq->{$idi}{$idj}{q} and {t} for each pair..
  
  my $Maxalt_snpvar= ($nparalog > 0 and $nparalog < NPARDUP) ? $MAXMAXALT_snpvar : $MINPAR_snpvar;
  my @snpd_keys = snp_distke('HD');# if(1 > $hdr); # if($TYDIST==2)  fixed
  
  print "#altpar_snp_dist_matrix: $otype, maxaltsnp=$Maxalt_snpvar, nid=$nids\n";
  my $pld="#s."; # ($prdistmat and $preqgene) ? "#s." : "";
  
  for (my $i=0; $i<$nids; $i++) { 
    my($idin,$idjn);
    my $idi=$ids[$i]; $idin=$idi;
    next unless($idi); # damn bug, in axt ??
    my $si="";
    if($alnIsPairs == 1) { } # need idj
    elsif($alnIsPairs == 3) { $si= $alnseq->{$idi}{$idi}; } 
    else { $si= $alnseq->{$idi}; }
    my(@alt,@par);
    
    for my $idj (@ids) { 
      next if($idj eq $idi); next unless($idj); # damn bug, in axt ??

      my $sj;
      my $orient=0; #  UPD23FEB20: $isrev=-1 if($alnseq->{$idi}{$idj}{orient} < 0);   # for $alnIsPairs == 1
      if($alnIsPairs == 1) {  $sj= $alnseq->{$idi}{$idj}{2};  $si= $alnseq->{$idi}{$idj}{1}; 
        # $orient=-1 if($alnseq->{$idi}{$idj}{orient} < 0); 
        $orient= $alnseq->{$idi}{$idj}{orient}||0;
        }
      elsif($alnIsPairs == 3) { $sj= $alnseq->{$idi}{$idj}; } 
      else { $sj= $alnseq->{$idj}; }
      unless($sj and $si) {
        # fixme how? blast aln  missing idj x idi due to low ident .. dummy zero align?
        print "#dbg: miss seq $idj:$sj x $idi:$si\n" if($debug >1);  
        $snpvals{$idi}{$idj}{ispar}= cUNK; # cMISS;
        next;
      }
      
      my @row= snp_distke($idi,$si,$idj,$sj); # orient?
      # snp_distke @ret=($nc,$nd,$pd,$da,$dsd,$dm,$dks,$oflag,@sdmore);  # ,  $ne,$ea,$esd,$em,$eks
      # snp_distke @hd=qw(Nc Nd pDsn aD2sn vD2sn mD2sn D2span Note); # drop: Ne aEsn vEsn mEsn Espan
     
      $idjn= $idj; # drop idin,idjn, same now as idi,idj
      
      # if(1 or $dopreqgene)   
      # like newick eqgene tables, need alt/par cutoffs for dsd >= 10?, pd=nd/nc >= 0.02..0.05 ?
      my($naln,$ndif,$pdif,$slocave,$snpvar,$slocmedn,$sspan,$sflag,@rmore)= @row;
      # my($dCodon, $iCBIN, $MMF)= (UPD23JAN01) ? @rmore: (0,0,0); #UPD23JAN01  dCodon iCBIN MMF
      # use this: $snpvals{$idi}{$idj}{$snpd_keys[$i]}= $row[$i]; 
           
      my $sskew= abs($slocave - $slocmedn);
       # ($slocave < 0.01 or $slocmedn<0.01)? SNP_HISKEW: ($slocave > $slocmedn)? $slocave/$slocmedn : $slocmedn/$slocave;

      my $pflag=""; # add out-flag to $ispar & $spoor ?
      my $spoor=0; 
      $spoor |= 1 if($sskew >= SNP_HISKEW);
      $spoor |= 2 if($ndif < SNP_LOWN); #? test naln?
      $spoor |= 4 if($sflag =~ m/NOMID/); #  DrOP: NOMIDsnp : bad? drop/replace w/ other? NOpar
      $spoor |= 8 if($sflag =~ m/NOpar/); # NOpar flag better than NOMIDsnp
      $spoor |= 16 if($sflag =~ m/YESpar/); ##?? add flag to spoor for YESpar, is other direction, ie sgood

      # NEED idmax cut opts .. >75 never par?, <50% mostly par, <40 almost always? <25 near certain
      # ** UPD snp_distke() ret to include idmax col;
      my $idmax= ($sflag =~ m/idx:([\d\.]+)/)? $1 : -1; #UPD  $sspot .= ",idx:$pmax_idspan%";
      $snpvals{$idi}{$idj}{idmax}= $idmax;
      
      ## UPD23Jan17: idmax becomes new classifier, add here or separetly? uses nparalog for ispar .. isalt midrange
      ##  snpdist_pairs() here should return 2 classifiers? and let classify() decide;
      ##  see apclass_idxdnacn.pl;
      ## 
      if($idmax >= SNP_IDMAX_ISALT) { $spoor |= 8; }
      elsif($idmax>=0 and $idmax <= SNP_IDMAX_ISPAR) { $spoor |= 16; } 
      if($orient < 0) { $spoor |= 16; } # add new asense flag? |32
      
      $pflag .= "pp$spoor" if($spoor); # add pflag for all $spoor ?
      
      # classify_ relies on this ispar val .. modify? 
      # use global const: ISPAR_DUNNO; ISPAR_CERT/LIKE/MAYBE? ISALT_CERT/LIKE/MAY
 
      my $ispar=($snpvar >= $Maxalt_snpvar)? cPAR : cALT; # FIXME merge below mods
      if($orient < 0) { $ispar=cPAR; $pflag .= ",asense"; } #??
      if($ispar == cPAR and ($spoor>0 and $spoor<16) and $nparalog < NPARDUP){ $ispar=cALT; }  #?? leave change to classify_ ? 
      
      # revise here to use treealn alt/par score, genex npar score
      # last step before dopreqgene should be alt/par classify from scores
      
      $snpvals{$idi}{$idj}{ispar}= $ispar;
      $snpvals{$idi}{$idj}{pflag}= $pflag;
      for my $i (0..$#row) { $snpvals{$idi}{$idj}{$snpd_keys[$i]}= $row[$i]; }
      
      if($ispar==cPAR) { push @par, "$idjn/$snpvar,$pdif$pflag"; } 
      elsif($ispar==cALT || $ispar==cUNK) {  push @alt, "$idjn/$snpvar,$pdif$pflag"; } #? ispar == cUNK also?
          
      if($prdistmat) {
        my @hd = snp_distke('HD');# if(1 > $hdr); # if($TYDIST==2)  fixed
        my $tag=($ispar==cPAR)?"Par":"Alt"; $tag.=$pflag;  
        
        my $iN= @hd - 1; $iN-- while($iN >= 0 and $hd[$iN] ne "Note");
        if($iN>=0){ my $nt=$row[$iN]; $row[$iN]= "$tag,$nt"; } 
        else { push @row,$tag; push @hd,"Note"; }
        
        print $pld.join("\t",qw(IDi_____ IDj_____),@hd),"\n" if(1 > $hdr++); #,'Tag'
        print $pld.join("\t",$idin,$idjn,@row),"\n"; #,$tag
        }
        
      }

      # if($preqgene) { # drop, or debug out? classify_ now does this table
      #   my $oid=$idin; #?? or $lid
      #   my $alts=(@alt<1)?"0":join(",",@alt);
      #   my $pars=(@par<1)?"0":join(",",@par);
      #   if($i == 0) {
      #   print "#".join("\t","PublicID","OID","AltIDs","Locus","Mapqual","ParalogIDs")."\n";  
      #   }
      #   print join("\t",$idi,$oid,$alts,0,0,$pars)."\n";
      # }
           
    } 
  return( $nids, \%snpvals);    
} 

=item snp_distke eqgene output

env otype=e debug=1 ./altparsnpce.pl tmpshi3c/EVGm000023.msa
#iname=tmpshi3c/EVGm000023.msa
#snp_dist_matrix: eqgene, nid=3
#PublicID	OID	AltIDs	Locus	Mapqual	ParalogIDs
AT1G64790t1	AT1G64790t1	AT1G64790t2/0.2,0.00214	0	0	AT1G64795t1/26.9,0.189
AT1G64790t2	AT1G64790t2	AT1G64790t1/0.2,0.00214	0	0	AT1G64795t1/26.9,0.189
AT1G64795t1	AT1G64795t1	0	0	0	AT1G64790t1/26.9,0.189,AT1G64790t2/26.9,0.189

=item snp_distke output

pt=EVGm000023; env debug=1 blastin=0 ../altparsnpcb.pl $pt.msa
#iname=EVGm000023.msa
IDi_____	IDj_____	Nc	Nd	pDsn	aD2sn	vD2sn	mD2sn	D2span
AT1G64790t1	AT1G64790t2	7929	17	0.00214	0.2	0.2	0.1	0,0.1,0.6
AT1G64790t1	AT1G64795t1	201	38	0.189	44.7	26.9	48.2	1.4,48.2,93
AT1G64790t2	AT1G64795t1	201	38	0.189	44.7	26.9	48.2	1.4,48.2,93

=cut

sub snp_distke { #  snp_diste, w/ doD=2 fixed, adding kurt(D2)
  my($id,$si,$jd,$sj)=@_; 
  
  use constant RETIDLEN => 0;
  my @hd=qw(Nc Nd pDsn aD2sn vD2sn mD2sn D2span Note); # drop: Ne aEsn vEsn mEsn Espan
  if(UPD23JAN01) { push @hd, qw( dCodon iCBIN MMF); }
  #UPD23JAN01: @ret add dcodon:25%,25%,50% == nd per 0,1,2 of codon, "icbin:$idbin", "mmf:$mmf";

  push @hd, "Nz", "zKurt$doKURT", "zrange" if($doKURT);
  # if(RETIDLEN) { push @hd, qw(IDi Leni IDj Lenj); } # drop this  
  return(@hd) if($id eq 'HD');
  
  #UPD23Jan06: add random err to bases, mimic rnaseq/heteroz effects on stats..
  use constant MUK => 10000;
  sub mutebase{ my $ri= rand(4); my $rb=($ri<=1)?"A":($ri<=2)?"C":($ri<=3)?"G":"T"; return $rb; }
  my( @muti, @mutj);
  if($ADDMUTES) {
    # addmutes == % mutations, 0.5% == 5/1000 bases change
    @muti=(0) x MUK;  @mutj=(0) x MUK;
    my $muter= $ADDMUTES/100 * MUK; # for mute chance
    for(my $i=0; $i<$muter; $i++) { my $mi= int(rand(MUK)); $muti[$mi]=1; my $mj= int(rand(MUK)); $mutj[$mj]=1;} 
    # for(my $i=0; $i<MUK; $i++) { my $mi= int(rand(MUK)); if($mi>=1 and $mi<=$muter) { $nmut++; $mute[$mi]=1;}  }
  }
  my $ni=length($si); my $nj=length($sj); my $ns=($ni>$nj)?$nj:$ni; 
  my($ldk,$nc,$eq,$nd,$ds,$dss, $es, $ess,$ne, $nik, $njk)=(0) x 19;
  my(@ds,@dk,@es,@ek,@sii,@sjj); @sii=();
  my @si=split"",$si; my @sj=split"",$sj;  
  my $atgk= -1; my @dcodon; # codon 0,1,2 snp
  my $atgi= index($si,'ATG'); my $atgj=  index($sj,'ATG'); 
  for(my $k=0; $k<$ns; $k++) { 
    my $bi=$si[$k]; my $bj=$sj[$k]; 
    $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
    # move dcodon here, gaps change codon %3 ..
    if($k == $atgi) { $atgk=$k; }
    #o: if($k == $atgi) { $atgk=@sii; } # test atgj ?
    if($ADDMUTES) {
      if($muti[$k] and $BASEOK{$bi}) { $bi= mutebase(); }
      if($mutj[$k] and $BASEOK{$bj}) { $bj= mutebase(); }
    }
    
    # FIXME option to keep not-ok '-' gaps here as part of pair-align measures
    # .. change BASEOK ?
    if($BASEOK{$bi} and $BASEOK{$bj}){ 
      push @sii,$bi; push @sjj,$bj; $nik++; $njk++; 
      my $neb= ($bi ne $bj);
      if($atgk >= 0 and $neb) { my $codon= ($k - $atgk) % 3; $dcodon[$codon]++; }
      } 
    elsif($BASEOK{$bi}) { $nik++; } elsif($BASEOK{$bj}) { $njk++;}
  }
  $ns= @sii; @si=@sii; @sj=@sjj; # note: $ns == final $nc here

  #UPD22DEC28: add new qual to s3spot (good par val): longest span of eq(i,j), some alts have snp both ends, largish alt exons
  # but alts also have longish span of eq(i,j), vs "good" pars
  my($idb,$ide,$max_idspan)=(-1,0,0,-1); # max_idspan/ns = %maxid, >50?% good alt indicator, needs test
  my($lneb,$bln,@bls)=(-1,0);
  my(@osi, @osj); # debug aln seq out
  for(my $k=0; $k<$ns; $k++){ 
    my $bi=$si[$k]; my $bj=$sj[$k]; 
    $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order    
    my $c= $k; #was $nc++; # here: $c == $k;
    my $neb= ($bi ne $bj)?1:0;
    
    if($neb){ $idb=-1; } else { 
      $ide= $k; $idb=$k if($idb<0);
      $max_idspan= _max($max_idspan,1 + $ide - $idb);
    }
    # count blocks eq and ne
    if($lneb == -1) {
      $bln=0; $bls[0]=0; $lneb=$neb;
    } elsif($neb eq $lneb) {
      $bls[$bln]++; # sum length-1
    } else {
      ++$bln; $bls[$bln]=0; # start new block eq/ne
    }
    $lneb=$neb;
    
    # $SNP1in3 doesn't reduce false-par calls much;
    # output aln suggest false-par related to snp at both ends, but not most-middle of alt-pair
    if($SNP1in3 and $neb) { $neb=(( $k>0 and $si[$k-1] eq $sj[$k-1]) and ($k < $ns-1 and $si[$k+1] eq $sj[$k+1])); }
    $osi[$k]= $bi; $osj[$k]= '.'; #debug
    if($neb) { # ($bi ne $bj) 
      my $d= int(1000*$c/$ns)/10;  # $doD==2 only
      $ds+=$d; $dss+=$d*$d; 
      push @ds,$d; 
      $nd++; $ldk=$c;
      $osj[$k]= $bj; 
      #bad here? if($atgk >= 0) { my $codon= ($k - $atgk) % 3; $dcodon[$codon]++; }
    }
  }
  @dk= @ds; $nc=$ns; # here: @dk == @ds
  ++$bln; # zero-base, $bln= @bls;
  # my $max_idspan= 1 + $ide - $idb; # not here, idb==-1 reset
  my $pmax_idspan= ($ns<1)?0: ($max_idspan >= $ns-2) ? 100: sprintf "%.1f",100*$max_idspan/$ns;
  
  my ($dzs,$nz,$kurt,$kurtvals,@dzs)=(0,0,0,0);
  if($doKURT) {
  # kurtosis calc from dz = (di - da)/dsd ; kurt= sum(dz^4)/n - 3
  # d == snp-neighbor dist, try also c/nc == %span
  my $da= ($nd<1)?0: $ds/$nd;  
  my $dsd= ($nd<2)?1: sqrt( ($dss - $ds*$ds/$nd)/($nd));
  for(my $k=0; $k<$ns; $k++){ 
    my $bi=$si[$k]; my $bj=$sj[$k]; 
    $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
    my $c=$k; 
    if($bi ne $bj) { 
      my $d= int(1000*$c/$ns)/10;  # $doD==2 only
      my $dz= ($d - $da)/$dsd;  $dzs += $dz**4; $nz++; push @dzs,$dz;
      } 
    } 
    if($nz>0) {
    $kurt= sprintf "%.3g", $dzs/$nz - 3; 
    my @m=(0,int($nz/4),int($nz/2),int(3*$nz/4),-1);
    $kurtvals= join ",", map{ sprintf "%.2f",$_ } @dzs[@m];
    }
  }
  
  my @sds=sort { $a<=>$b } @ds; # @ds now should be same order
  my $da= ($nd<1)?0: sprintf("%.1f",$ds/$nd); # da == ave_dsnip, ds == sum
  # my $dsd= ($nd<2)?0: sprintf "%.1f", sqrt( ($dss - $ds*$ds/$nd)/($nd-1)); # err sqrt( -0.000n)
  my $dvar=($nd<2) ? 0 : ($dss - $ds*$ds/$nd)/($nd-1); 
  my $dsd= ($dvar<0.0001) ? 0 : sprintf "%.1f",sqrt($dvar);
  my $m= int($nd/2); 
  my $dm= ($nd<1)?0:$sds[$m]; 
  my $pd= ($nc<1)?0: sprintf("%.3g",$nd/$nc); 
  
  my @m= map{ int($_ * $nd) } (0.01,0.25,0.50,0.75,0.99); # (0.01,0.10,0.20,0.30,0.40,0.50,0.60,0.70,0.80,0.90,0.99);
  my $dks= ($nd<1)?"0,0,0":join(",",@dk[@m]); # this, snp-span / total span (nc) may be useful

  # problem false-par cases: end snps but no middle snp:
  #  dsd high, dmed < 10 or dm > 90, dav > 10+dmed, @dk most at 1 end (dm), few at 2nd end, no middle.
  my $oflag="";
  use constant snTEST_MIDSNP => 0; # drop, not good
  if(snTEST_MIDSNP and ($dm < 10 or $dm > 90) and $dsd >= $MINPAR_snpvar) {
    my($nlo,$nmi,$nhi)=(0) x 3;
    foreach my $d (@ds) { if($d<10){ $nlo++; } elsif($d>90){ $nhi++; } else { $nmi++; } }
    if($nmi < 1 or ($nmi < 0.01*$ns)) {
      $oflag.="NOMIDsnp,";
    }
  } 
  
  # 2. alt/par spread test, count snp outside dmedn +/- 9%
  # not sure this is useful.. Yes,  mostly right.
  use constant kSPWID => 11; # 9;
  my($dmom, $dmom2, $dsmedn, $dsoutn, $dsout2n)=(0) x 9;
  my @dsmed= grep{ $_ > $dm - kSPWID  and $_ < $dm + kSPWID } @sds;
  my @dsout= grep{ $_ <= $dm - kSPWID  or $_ >= $dm + kSPWID } @sds;
  $dsmedn= @dsmed; 
  $dsoutn= @dsout;
  if($dsoutn>1) {
    $dmom= $dsout[ int($dsoutn/2) ];  # repeat cut dmom +- kSPWID, more?
    my @dsout2= grep{ $_ <= $dmom - kSPWID  or $_ >= $dmom + kSPWID } @dsout;
    $dsout2n= @dsout2;  if($dsout2n>0) { $dmom2= $dsout2[ int($dsout2n/2) ]; }
  } 
  
  # add pmax_idspan here as sspot qual : YES, valuable
  my $sspot=($dsout2n>0) ? "s3spot" : ($dsoutn>1) ? "s2spot" : ($dsmedn>0) ? "s1spot" : "s0spot" ;
  $sspot .= ",$dm:$dsmedn" if($dsmedn>0);
  $sspot .= ",$dmom:$dsoutn" if($dsoutn>0);
  $sspot .= ",$dmom2:$dsout2n" if($dsout2n>0);
  $sspot .= ",idx:$pmax_idspan%";
  #?? override MINPAR_snpvar for s3spot + pmax_idspan < MIN_ALT_IDSPAN
  if( $dsd >= $MINPAR_snpvar) { # only flag spots for large var? or allow s3spot ? want NOpar to cancel large snpvar
    $oflag .= ($sspot =~ /s3spot/)?"YESpar:" : ($sspot =~ /s[01]spot/)?"NOpar:": "nopar:"; # nopar==maybe-no,maybe-yes-par
  }
  $oflag .= $sspot; # always add? YES, sNspot and idx: deserve own @ret vals ??
  
  #   my $ea= ($ne<1)?0: sprintf("%.1f",$es/$ne); my @ses=sort { $a<=>$b } @es;
  #   my $esd= ($ne<2)?0: sprintf "%.1f", sqrt( ($ess - $es*$es/$ne)/($ne-1));
  #   my $m= int($ne/2); my $em= ($ne<1)?0:$ses[$m]; 
  #   my $eks= ($ne<1)?"0,0,0":join(",",@ek[0,$m,-1]); # this, snp-span / total span (nc) may be useful
  
    # alts have short snp-span, pars long span .. count of difs, nd / nc about same as global 1 - sim
    # a couple cases of alt/par w/ similar nd, alts have shorter snp-span / longer align span (nc), pars snp-span ~= align-span
  my @ret=($nc,$nd,$pd,$da,$dsd,$dm,$dks,$oflag);  # ,  $ne,$ea,$esd,$em,$eks
  
  #UPD23JAN01: @ret add dcodon:25%,25%,50% == nd per 0,1,2 of codon, "icbin:$idbin", "mmf:$mmf";
  # opt above: just d3codon
  if(UPD23JAN01) {
    my $dcodon=""; # "dcodon:"; # dcodon:25%,25%,50% or counts 5,5,10 ?
    if($nd>0) { # nd > 2 ? require minimal diff
      $dcodon .= join",", map{ my $dc= $dcodon[$_]||0; my $pc=sprintf"%.0f",100*$dc/$nd; $pc; } (0,1,2);
    } else { $dcodon.="33,33,33"; } #?  was 0,0,0
    push @ret, $dcodon;
    
    # IsoSVM doi:10.1186/1471-2105-7-110 define inv(CBIN) and MMF from eq/ne blocks
    my($icbin, $mmf)= (0,0);
    if($bln > 0 and $nc > 0) { # must be at least 1 bln
      # do SCALE_MMF here ? # if(SCALE_MMF) { ($iCBIN, $MMF)= map{ sprintf "%.1f",100*$_; } ($iCBIN, $MMF); }
      $icbin= (SCALE_MMF) ? sprintf "%.2f", 100/$bln : sprintf "%.4g", 1/$bln; # may have << 0.001 vals
      $mmf =0; for(my $i=0; $i<$bln; $i++) { $mmf += $bls[$i] }; 
      $mmf = (SCALE_MMF) ? sprintf "%.2f", 100*$mmf/$nc : sprintf "%.3f",  $mmf/$nc; # sum block len-1 / nbases
    }
    push @ret,$icbin, $mmf; # "icbin:$icbin", "mmf:$mmf";

  }
  
  push @ret, $nz, $kurt, $kurtvals if($doKURT);
  # if(RETIDLEN) { push @ret, $id, $nik, $jd, $njk; } # maybe $nik, $njk = len of okbases i,j ;  
  if($debug>9) {
    use constant OW=>100;
    for(my $i=0; $i<$ns; $i+=OW) {
    my $j=$i+OW; $j=$ns-1 if($j>=$ns);  
    my $osi=join"",@osi[$i..$j]; my $osj=join"",@osj[$i..$j];
    print "#d $id: $osi\n";
    print "#d $jd: $osj\n";
    }
  }
  return @ret; 
} 


#========= ALIGN TREE subs (from newickparse.pl) =========
sub _min { return ($_[1] < $_[0]) ? $_[1] : $_[0]; }
sub _minNotZero{ return  ($_[1] > 0 && ($_[1] < $_[0] || $_[0] == 0)) ? $_[1] : $_[0]; }
sub _max { return ($_[1] > $_[0]) ? $_[1] : $_[0]; }

sub parseAlnTree {
  my($iname)=@_;

  my $doprtdist=($debug>1); #??
  return (0) unless(-s $iname);
  my $inh; open($inh,$iname) or return undef;
  my(@t)= readAlnTree($iname,$inh);
  
  my($rootc,$idn,$lidn,$tmore)= nkpar2c(@t);
  # nkpar2c returns tree data: 
  #   node = { id=>0, lid=>0, len=>0, lev=>0, grp=>0, it=>0, lnd=>undef, nnd=>undef, nds=>[] }
  #   \%idn  = node{id} hash, descend thru root.nds[], ascend thru lnd (last/parent node)
  #   \@lidn = node[0..n] using lid= local id; 
  #   $root  = base node

  my @ids= sort keys %$idn; 
  my $nid=@ids;
  
  # my($dsum,$nbr,$lidp)=
  tre_distsum($rootc);
  for my $id (@ids) { tre_nodelen($idn->{$id}, $lidn); }
  
  if($doprtdist){ print "#tree_altpar_table=\n"; trepr_distsum($rootc);  }

  #? print "#... dist2 : common_branch ...\n" if $debug;
  my (%cbdist,%cbval); my $maxdif=0;
  for my $id (@ids) {
    my $idnode= $idn->{$id};
    for my $jd (@ids) {
      my $jdnode= $idn->{$jd};
      my($clid,$cndif,$clidp)= common_branch($idnode, $jdnode, $lidn);
      $cbdist{$id}{$jd}{dist}=$cndif; $cbdist{$id}{$jd}{clid}=$clid; $cbdist{$id}{$jd}{clidp}=$clidp;
      $cbval{$clid}=$cndif;
      $maxdif= _max($cndif,$maxdif);
    }
  }
  # @tree_distvals= qw( dist clid clidp );
  
  my $MAXD=1; 
  #fixme: if($otype eq 'sim' and $maxdif > 1){ $MAXD=$maxdif; } #?

  #insert1: est. common_branch_altpar_cut, nparalog from  dMINALT=1.0 - $MINALT/100
  # using sorted cbval, for MINALT guesstimate in 90..99 range, not reliable
  # print "#tree_common_branch_altpar_cut: $np:$simcut:$cbcut (n:simcut:combr), for options pminalt=$MINALT, nparalog=$nparalog \n";
  
  #insert2. look for common branch distances gt than MIN_MINALT, ie distance must be paralog
  # $dMIN_MINALT= 1.0 - $MIN_MINALT/100 for  MIN_MINALT <= 90, *should be* reliable
  # print "#tree_altpar_mustcut: new npar=$np1, $np:$simcut:$cbcut (n:simcut:combr), for options pminminalt=$MIN_MINALT (lowest alt ident), nparalog=$nparalog \n";

  # score common branches, including leaves (gene id seqs), and classify to alt/par from treedist cut point 
  # my(%cbalt,%cbpar); << merge to one hash by gene @ids,  use %cbdist or %cbval of above?
  # ^^ this should be main result of parseAlnTree(), then combine w/ snp_dist evd, genex dna evd
  
  my $npartree= ($nparalog>=1.5)? int(0.51+$nparalog) : ($nparalog>0)? int(0.49+$nparalog) : 0 ;
  if(1) { ## was ($nparalog>=1)   # or xxx, find common branches for nparalog
    my @cbalt= sort { $cbval{$b} <=> $cbval{$a} or $a <=> $b } grep { $_ ne "0" } keys %cbval; # order by largest commbr dist
    
     # insert1+2 : MIN_MINALT lower pident bound for alt cutoff, ie below MIN_MINALT/above dMINMIN is always paralog..
    my $dMIN_MINALT= 1.0 - $MIN_MINALT_tree/100;
    my $dMINALT= 1.0 - $MINALT_tree/100;
    
    my ($np,$cbi,$cbcut,$dcut)=(1,0,0,0); 
    for(my $i=0; $i<@cbalt; $i++){ my $cb=$cbalt[$i]; 
      if($cbval{$cb} < $dMIN_MINALT) { $cbi=$i; $cbcut=$cb; $dcut=$cbval{$cb};  last; } else { $np++; } 
    }
    
    my ($np2,$cbi2,$cbcut2,$dcut2)=(1,0,0,0); 
    for(my $i=0; $i<@cbalt; $i++){ my $cb=$cbalt[$i]; 
      if($cbval{$cb} < $dMINALT) { $cbi2=$i; $cbcut2=$cb; $dcut2=$cbval{$cb};  last; } else { $np2++; } 
    }
    
    # test orig npar on MINALT_tree, ie turn off npar>1 unless global align below MINALT
    #orig: if($npartree >= 2 and $npartree > $np2) { $npartree= $np2; } 
    if($npartree >= 2 and $npartree > $np2) { $npartree= _max(2,$np2); } # keep npartree >= 2 here, ie trust that vs MINALT
    
    # test on MIN_MINALT_tree, turn on npar>1 if below np.min_minalt
    if($np > $npartree) { 
      my($simcut)= map{ sprintf"%.2f%%", 100*($MAXD - $_) } ($dcut); 
      print "#tree_altpar_mustcut: new npar=$np:$simcut:$cbcut (n:simcut:combr), for options pminminalt=$MIN_MINALT_tree (lowest alt ident), nparalog=$npartree \n"
        if($debug);
      $npartree= $np;  
    }
    
    # instead of this, set ispar_tree in cbdist{id}{jd}{ispar}
    my @cbpar= ($npartree<2)?() : splice(@cbalt,0,$npartree-1);
    my %cbpar= map{ $_ => $cbval{$_} } @cbpar; # key=>val here are tree-localid => branch-dist (>0?)
    my %cbalt= map{ $_ => $cbval{$_} } @cbalt;
    for my $id (@ids) {
      for my $jd (@ids) { next if($jd eq $id);
        my $clid= $cbdist{$id}{$jd}{clid};
        my($ispar,$isalt)=(-1,-1);
        if( my $v= $cbpar{$clid} > 0 ) { $ispar=$v;  $cbdist{$id}{$jd}{isparval}= $v; } 
        elsif( my $v= $cbalt{$clid} >0 ) { $isalt=$v;  $cbdist{$id}{$jd}{isparval}= $v; }
        # classify_ relies on this ispar val .. modify? 
        # use global const: ISPAR_DUNNO; ISPAR_CERT/LIKE/MAYBE? ISALT_CERT/LIKE/MAY
        $cbdist{$id}{$jd}{ispar}= ($ispar>0)? cPAR : ($isalt>0)? cALT: cUNK; #??
      }
    }
        
    # print "#tree_common_branch_paralogs: par=$npar:$cbparlo:$cbparv alt=$nalt:$cbalthi:$cbaltv (n:simcut:combr), for nparalog=$npartree \n";
  }
 
  @atreedist_keys= qw( clid clidp  dist ispar isparval ); # for atree_pairdist->{id}{jd}{@keys}
  # unused: cbdpar cbdalt .. changed to isparval
  # NOW have global @atreedist_keys = value keys in atree_pairdist->{id}{jd}{@keys}
  
  # optional output sim/dist matrix
  # print "#tree_common_branch_matrix: similarity/distance, nleaf=$nid\n";

  # opt output: eqgene pairwise table of id, alts, pars
  # print "#tree_common_branch_matrix: $otype, nleaf=$nid\n";
  
  return($nid, \%cbdist, \%cbval, $rootc, $idn, $lidn); #?? also $rootc,$idn,$lidn ?
}

sub commonBranch_of_nParalog {
  my($nparat, $cbvalh, )= @_;
  
  my @cbalt= sort { $cbvalh->{$b} <=> $cbvalh->{$a} or $a <=> $b } grep { $_ ne "0" } keys %{$cbvalh}; # order by largest commbr dist
  
  if($nparat < 1) { return(0,0,{}); } #?? nparat <=1 ?
  elsif($nparat > @cbalt) { $nparat=@cbalt; }
  my $npi= $nparat - 1;
  my $cbid= $cbalt[$npi]; #? this is 1st alt, not last par? should be nparat - 2 ?
  my $cbdist= $cbvalh->{$cbid}; # this is 1st alt dist ?
  my %cbpar;
  for(my $i=0; $i<@cbalt; $i++){ my $id=$cbalt[$i]; 
    $cbpar{$id}= ($i<$npi)?1:0; # ?? change to cbpar{id} = cPAR:cALT
    } # ispar for i<npi seems right, for NOROOT 
  return($cbid, $cbdist, \%cbpar); # ret \@cbpar, \@cbalt ??
}

sub readAlnTree {
  my($iname,$inh)=@_;
  
  print "#itreename=$iname\n"; # if($debug); #  or $doprt
  my $t=""; my %an=(); my @com;
  while(<$inh>){ 
    if(/^\w/){ my($id,$an)=split" ",$_,2;
      if($an){ my($di)= $an=~ s/(:[\de\.\-]+)\s*$//?" $1":""; $an{$id}=$an; $_="$id$di\n"; } 
    } elsif(s/(#.*)$//){ push @com,$1; } 
    s/\s*\n/ /; $t.=$_; 
    # one newick tree ends at ');' .. stop here? or nkpar2c(@t)
  } 
  
  my @t=split/([\(\)\,])/,$t; 
  for my $i (0..$#t) { 
    if($t[$i] =~ /^\)/){ if($t[$i+1] =~ /:([\de\.\-]+)/) { $t[$i].=$t[$i+1]; $t[$i+1]=undef; } }
  }
  print "#itree=",join("",@t),"\n" if($debug); # $doprt

  return(@t);
}

=item nkpar2c parse newick tree

  nkpar2c returns tree data: 
    node = { id=>0, lid=>0, len=>0, lev=>0, grp=>0, it=>0, lnd=>undef, nnd=>undef, nds=>[] }
    \%idn  = node{id} hash, descend thru root.nds[], ascend thru lnd (last/parent node)
    \@lidn = node[0..n] using lid= local id; 
    $root  = base node

=cut

sub nkpar2c {
  my @t=@_;
  # print "#nkpar2\n" if $debug;

  # drop nnd? nds always added to
  my $root={ id=>0, lid=>0, len=>0, lev=>0, grp=>0, it=>0, lnd=>undef, nnd=>undef, nds=>[] };
  my (%idn,@lidn); # @ids;
  my $lnd=$root; @lidn=( $root );
  my ($lev,$grp,$it,$lid,$trestop,$tat)=(0) x 9;
  
  # one newick tree ends at ');' .. stop there.. if more @t, return what?
  foreach (@t) { 
    $tat++; next unless(defined $_);
    s/^\s+//; s/\s+$//;
    my $di= ($_ =~ s/\s*:([\de\.\-]+)$//)?$1:undef;  
    # my $nd={ id=>0, len=>0, lnd=>undef, nnd=>undef, lev=>0, it=>0, nds=>[] };
    # $nd->{lnd}= $lnd;  $nd->{lev}=$lev;  $nd->{grp}=$grp; # $nd->{it}=$it;
    # if(defined $di){  $di="0" if($di eq "-0"); $nd->{len}= $di; }
    
    my $nd= undef;
    if(/^\(/ or /^\w/){ 
      $nd={ id=>0, lid=>0, len=>0, lev=>0, grp=>0, it=>0, lnd=>undef, nnd=>undef, nds=>[] };  #newnode; drop nnd
      $nd->{lid}= ++$lid; $nd->{lnd}= $lnd;  $nd->{lev}=$lev;  $nd->{grp}=$grp; # $nd->{it}=$it;
      $lidn[$lid]=$nd;
      if(defined $di){  $di="0" if($di eq "-0"); $nd->{len}= $di; }
    }
    
    if(/^\(/){ $nd->{lev}= ++$lev; $nd->{grp}= ++$grp; $lnd->{nnd}= $nd; push @{$lnd->{nds}}, $nd;  $lnd=$nd; $it= $lnd->{it}; }
    elsif(/^\)/) { $nd= $lnd || $root; $nd->{grp}= ++$grp; $lnd=$nd->{lnd}; $lev=$lnd->{lev}; $it= $lnd->{it}; 
      if(defined $di){ $nd->{len}+=$di; } 
      if(m/\)\s*;/){ $trestop=$lid; last; }
      }
    elsif(/^,/){ $it= ++$lnd->{it}; } # drop replace w/ @nds count?
    elsif(/^\w/) { 
      my($id,$an)=split" ",$_,2; $nd->{id}=$id; $nd->{it}=$it; 
      $idn{$id}=$nd; #push @ids, $id;
      $lnd->{nnd}=$nd; push @{$lnd->{nds}}, $nd;  
    }
  }
  
  my @tmore = ($trestop > 0 and $tat < @t) ? splice(@t,$tat) : ();
  return ($root, \%idn, \@lidn, \@tmore);
}

sub tre_distsum {
  my ($thisnd,$dsum,$nbr,$lidp)=@_;
  my @ndkey= qw( id lid len lev  lnd nds );  
  my($id, $lid, $len, $lev, $lnd, $nds)= map{ my $v=$thisnd->{$_}; $v="0." unless(defined $v); $v; } @ndkey;
  
  $dsum += $len;
  $lidp .= "$lid,";
  $thisnd->{dsum}= $dsum;
  $thisnd->{nbr}= ++$nbr;
  $thisnd->{lidp} = $lidp;

  if($nds) { # recurse on child nodes
    for my $nd (@$nds) { my($tsum,$tbr,$tlidp)= tre_distsum($nd,$dsum,$nbr,$lidp); }
  }
  
  return ($dsum,$nbr,$lidp);
}

# for $id (@ids) { tre_nodelen($idn->{$id}, $lidn); }
sub tre_nodelen {
  my($thisnd, $lidn)=@_;
  
  my @ndkey= qw( id lid len lev  lnd nds dsum nbr lidp );  
  my($id, $lid, $len, $lev, $lnd, $nds, $dsum, $nbr, $lidp)= 
    map{ my $v=$thisnd->{$_}; $v="0." unless(defined $v); $v; } @ndkey;
  # $nd={ id=>0, lid=>0, len=>0, lev=>0, grp=>0, it=>0, lnd=>undef, nnd=>undef, nds=>[] };  #newnode; drop nnd
  
  my @lidp= reverse split",",$lidp;
  for my $li (@lidp) {
    my $ndl= $lidn->[$li] or next;
    my $ldsum= $ndl->{dsum};
    my $ndif= $dsum - $ldsum;
    if( my $ondif= $ndl->{ndif}) { $ndif=  _max($ondif,$ndif); }
    $ndl->{ndif}= $ndif;
  }
}

sub common_branch {
  my($nda, $ndb, $lidn)=@_;
  my($clid, $clidp, $cndif, $cnode)= (0) x 3;

  my @ndkey= qw( lid lev ndif lidp );  
  my( $lida, $leva,  $ndifa, $lidpa)= 
    map{ my $v=$nda->{$_}; $v="0." unless(defined $v); $v; } @ndkey;
  my( $lidb, $levb,  $ndifb, $lidpb)= 
    map{ my $v=$ndb->{$_}; $v="0." unless(defined $v); $v; } @ndkey;
  if($lida eq $lidb){ return($lida, $ndifa, $lidpa, $nda); }
  
  my @lidpa=split",",$lidpa; my @lidpb=split",",$lidpb; my $nlid=@lidpa;
  my $i=0; for(; $i<$nlid; $i++) { if($lidpa[$i] ne $lidpb[$i]) { $i-- if($i>0); last; } }
  $clid= $lidpa[$i]; $cnode= $lidn->[$clid]; $cndif=$cnode->{ndif}||0;
  $clidp= join",",@lidpa[0..$i];
  return($clid, $cndif, $clidp, $cnode);
}

sub trepr_distsum {
  my ($nd)=@_;
  my @ndkey= qw( lid lev dsum nbr ndif lidp id );  
  my($np)= trepr_walk(\@ndkey,$nd,1, 0);
  return($np);
}

sub trepr_walk {
  my ($ndkey,$nd,$recurs,$ip)=@_;
  ##    $nd={ id=>0, lid=>0, len=>0, lev=>0, grp=>0, it=>0, lnd=>undef, nnd=>undef, nds=>[] };  #newnode; drop nnd
  my @nv= map{ my $v=$nd->{$_}; $v="0." unless(defined $v); $v=sprintf("%.5g",$v) if(/^(dsum|ndif)/); $v; } @$ndkey;
  my $nds= $nd->{nds}; my $nnd= $nd->{nnd};
  $ip||=0; unless($ip){ print "#tw.".join("\t","iPr",@$ndkey)."\n"; }
  print  "#tw.".join("\t",$ip,@nv)."\n";
  if($recurs and $nds) {
    for my $nd (@$nds) { trepr_walk($ndkey,$nd,1,++$ip); }
  }
  return ($ip);
}  


sub read_genexcopy { # see tr2dupgeneclass1a.pl for more data
  my($inf,)=@_;
  my($ngenex,$npar,$nuni,$nothr,%tcopy,%clen)=(0)x 4;

  # my($ok,$inh)= openRead($inf);  
  open(my $inh, $inf) or return(0);
  while(<$inh>) {
    next if(/^\W/); chomp; my @v=split"\t"; 
    my($id,$cw,$tcopy,$ucla)=@v[0,1,4,6]; 
    if($tcopy >= NPARDUP){ $npar++; } elsif($ucla =~ /uniq/){ $nuni++; } else { $nothr++; }
    $tcopy{$id}=$tcopy; $clen{$id}=$cw; $ngenex++;
  } close($inf);
  print("#read_genexcopy ngene=$ngenex (u:$nuni,p:$npar,o:$nothr) of $genexcopy\n") if($debug);
  return($ngenex, \%tcopy, \%clen);
}


=item read_axt as of lastz pair-align

  ($nid_aln, $alnseq)= read_axt($inh,$iname); 
   
  $bg/mb/bin/lastz --format=axt --strand=plus --notrivial --nomirror --self \
    --output=tmps/HsasmEVm005164.pairlz 'tmps/HsasmEVm005164.cds[multiple]' 
  ** change: --strand=both and parse aln +/- for antisense paralogs;
  ** store aln span:or in pairseq{id}{jd}{aloc1,aloc2} ? {aspan1,2} ? and {score}
  .. drop -notriv -nomirr and maybe drop below mirr

  lastz --format=axt --strand=both --self --output=tmps/$evid.pairlz "tmps/$evid.cds[multiple]"

  axt record 3 lines: 
    i qid qb qe sid sb se sor score
    qid1aln
    sid2aln
     .. i=align rec num, int 0..n-1
     .. sor = +/- align orient
     .. score=int, chose max score for multiple aligns of same qid,sid
 
  top comments
    # lastz.v1.03.54 --format=axt --strand=plus --notrivial --nomirror --self --output=tmps/HsasmEVm005164.pairlz 
    #
    # hsp_threshold      = 3000
    #.. opt vals, no value
     
=cut

sub read_axt {
  my($inh,$iname)=@_;
  my($qid,$sid,$at,$nid_aln,$nsubaln,)=(0) x 9;
  my(%pairseq,%ids);
  print "#read_axt($iname)\n" if($debug);
  while(<$inh>) {  
    if(/^#/) { } # ignore comm ?
    elsif(/^\d/){  # /^\d+\s+\S+\s+\d+/
      my($it,$id,$ib,$ie,$jd,$jb,$je,$jor,$score)=split;
      my $skip=0; 
      if(my $osc=$pairseq{$id}{$jd}{score}) { $skip=1 if($osc >= $score); }
      if($skip) { 
        $nsubaln++; $at=0; $qid=$sid=0;
      } else {
        $at=1; $qid=$id; $sid=$jd;  
        $ids{$qid}++; $ids{$sid}++; 
        $pairseq{$qid}{$sid}{score}=$score; 
        $pairseq{$qid}{$sid}{orient}= ($jor eq '-')?-1:1; # $jor; or {rev}= ($jor eq '-')?1:0;
        $pairseq{$qid}{$sid}{span1}="$ib $ie";
        $pairseq{$qid}{$sid}{span2}="$jb $je";
      }
    } 
    elsif(/^\S/ and $at==1) { chomp; $pairseq{$qid}{$sid}{1}=$_; $at++; } #  and $qid and $sid
    elsif(/^\S/ and $at==2) { chomp; $pairseq{$qid}{$sid}{2}=$_; $at++; } #  and $qid and $sid
    elsif(/^\s/){ $at=0; $qid=$sid=0; }
  }
    
  $nid_aln= scalar(keys %ids);   
  # do mirror in case of --nomirror
  my @ids=sort keys %ids; my $nfix=0;
  for my $id (@ids) { for my $jd (@ids){ next if($id eq $jd);
    if($pairseq{$jd}{$id}{1} and not $pairseq{$id}{$jd}{1}) { 
      $pairseq{$id}{$jd}{2}= $pairseq{$jd}{$id}{1};  
      $pairseq{$id}{$jd}{1}= $pairseq{$jd}{$id}{2};
      $pairseq{$id}{$jd}{span2}= $pairseq{$jd}{$id}{span1};
      $pairseq{$id}{$jd}{span1}= $pairseq{$jd}{$id}{span2};
      $pairseq{$id}{$jd}{orient}= $pairseq{$jd}{$id}{orient};
      $pairseq{$id}{$jd}{score}= $pairseq{$jd}{$id}{score};
      $nfix++;
      }
    }
  }
  print "#read_axt nid_aln=$nid_aln\n" if($debug);
  print "#axt_pairseq mirrored=$nfix\n" if($debug and $nfix>0);
  return($nid_aln, \%pairseq);
}


#========= exonerate-input sub, like read_blast3fmt ===========

=item exonerate fmt

exonerate --model affine:local --showalignment yes --alignmentwidth 10000 --showcigar no --showvulgar no  \
  tmps4d/EVGm005481.cds   tmps4d/EVGm005481.cds |\
  egrep -v 'Alignment:|Model:|score:|range:' | sed 's/type=CDS.*//;' |  less
# drop: --ryo ">%qi.%ql %ti.%tl %ei/%et %pi\n"  

         Query: AT2G27790t4 hdr ...
        Target: AT5G27300t1 hdr info..

  599 : AGA-GGTTACAACC---AAGCAGCCAAACACAACC----TGAAGAAAGTGGTAACCAAAAGAGGTTACAACCAAGCAGCCAAATACAACCTGACTTGAAGAGAACCCGAGTGGATAACCGAAGAAGAATATAA :  723
        ||| ||||| |||    | | ||  |||| |||      |||||||||||||||||||||||||||||||||||||||||||| |||||||||||| |||||||||||||| |||||||||||||||||||||
 1523 : AGATGGTTAAAACTGTGAGGGAG--AAACTCAATAAGATTGAAGAAAGTGGTAACCAAAAGAGGTTACAACCAAGCAGCCAAACACAACCTGACTTCAAGAGAACCCGAGTCGATAACCGAAGAAGAATATAA : 1653

.. full xon.out default w/ cigar (not useful here)
C4 Alignment:
------------
         Query: AT2G27790t2 type=CDS; aalen=190,55%,complete-utrpoor; clen=1035; strand=+; offs=237-809; codepot=Code/0.012; oid=at18nctair:g817326t2 geneid=817326; db_xref=Araport:AT2G27790,TAIR:AT2G27790,ncbiprot:NP_001324573.1,ncbirna:NM_001336126; isoform=2; Name=RNA-binding (RRM/RBD/RNP motifs) family protein; 
        Target: AT5G27300t1 type=CDS; aalen=550,98%,complete; clen=1686; strand=+; offs=1-1653; codepot=Code/0.015; oid=at18nctair:g832788t1 geneid=832788; db_xref=Araport:AT5G27300,TAIR:AT5G27300,ncbiprot:NP_001154742.1,ncbirna:NM_001161270; isoform=1; Name=pentatricopeptide (PPR) repeat-containing protein; 
         Model: affine:local:dna2dna
     Raw score: 453
   Query range: 448 -> 573
  Target range: 1522 -> 1653

  449 : AGA-GGTTACAACC---AAGCAGCCAAACACAACC----TGAAGAAAGTGGTAACCAAAAGAGGTTACAACCAAGCAGCCAAATACAACCTGACTTGAAGAGAACCCGAGTGGATAACCGAAGAAGAATATAA :  573
        ||| ||||| |||    | | ||  |||| |||      |||||||||||||||||||||||||||||||||||||||||||| |||||||||||| |||||||||||||| |||||||||||||||||||||
 1523 : AGATGGTTAAAACTGTGAGGGAG--AAACTCAATAAGATTGAAGAAAGTGGTAACCAAAAGAGGTTACAACCAAGCAGCCAAACACAACCTGACTTCAAGAGAACCCGAGTCGATAACCGAAGAAGAATATAA : 1653

cigar: AT2G27790t2 448 573 + AT5G27300t1 1522 1653 + 453  M 3 D 1 M 10 D 3 M 6 I 2 M 10 D 4 M 94

=cut



sub read_exonerate {
  my($inh,$iname)=@_;
  my($qty,$qid,$qb,$qseq,$qe,$sid,$sb,$sseq,$se,$inq, $qna, $isq,$ilen)= (0) x 19;
  print "#read_exonerate($iname)\n" if($debug);
  
  #no: $BASEOK{'.'} = 1;
  my($nids,$nalnc,$npairs,%pairseq,%pairbe)= (0,0,0); # %qna, 
  while(<$inh>) {
  
    ## exonr has 2+ aligns per qid,sid .. longest 1st, skip others?? or paste together, need qb,e; sb,e .. 
    ## ** Try adding all aligns for pairseq, drop inq=-1
    
    if(/^C. Alignment/) { # C4 Align .. other?
      ($qty,$qid,$qb,$qseq,$qe,$sid,$sb,$sseq,$se,$inq, $qna, $isq,$ilen)= (0) x 19;
      $nalnc++;
      
    } elsif(/^\s*(Query|Target): (\S+)/){ 
      $qty=$1; my $id=$2; # maybe parse evg hdr: aalen|clen|offs ..
      if($inq > 2) { } # last pairseq
      if($qty eq "Query") { $qid=$id; $qseq=""; $qb=$qe=0; $inq=1; $nids++; } 
      else { $sid=$id; $sseq=""; $sb=$se=0;
       if($pairseq{$qid}{$sid}{2}) { $inq=-1; } #2nd aln, skip??
       else { $inq=2; $nids++; $npairs++; }
      }
      
      # ($qid,$qb,$qseq,$qe)=split" ",$_; $qid=~s/Query_/Q/; 
      # $qna{$qid}=$qna if($qna); $inq=$qid; $qna=0; $qseq=uc($qseq);
      # $pairseq{$qid}{$qid} .= $qseq;  $isq= index($_,$qseq); $ilen=length($qseq); 
      # print "# $qid:$pairseq{$qid}{$qid}\n" if($debug>1);
      
    } elsif(/^\s*(Model|Raw score|Query range|Target range)/) { 
      # skip
      
    } elsif($inq>1 and /^\s*(\d+) : (\S[^:\s]*)/ ) { 
      my($ab,$as)=($1,$2); $as=uc($as); 
      my $al=length($as); my $ae=$ab + $al - 1;
      if($inq % 2 == 0) { 
        if($qb<1){ $qb=$ab; } $qe=$ae; 
        ## FIXME, cant use qid,qid here: overwrite other qid,sid 1st seq
        ## need $pairseq{$qid}{$sid}{qs} and {ts} ?
        $pairseq{$qid}{$sid}{1} .= $as; # $qseq.= $as; 
        $pairbe{$qid}{$sid}{qb}=$ab;  $pairbe{$qid}{$sid}{qe}=$ae; 
      } else {   
        if($sb<1){ $sb=$ab; } $se=$ae; 
        $pairseq{$qid}{$sid}{2} .= $as; #$sseq.= $as;
        $pairbe{$qid}{$sid}{sb}=$ab;  $pairbe{$qid}{$sid}{se}=$ae; 
      } 
      $inq++;
    }
    
    # elsif($inq and /^(Subject|\w)/) { 
    #   ($sid,$sb,$sseq,$se)=split" ",$_;  $sid=~s/Subject_/S/;
    #   $sseq= substr($_,$isq,$ilen); $sseq =~ s/ /\-/g;  $sseq=uc($sseq);
    #   # print "$sid:$sseq\n" if($debug>1);
    #   # BUG here, sseq dont all start at same qb-base
    #   # $pairseq{$qid}{$sid} .= $sseq; 
    #   
    #   my $s=$pairseq{$qid}{$sid}; 
    #   my $sl=length($s); my $lpad= $qb + $ilen -1 - $sl; 
    #   if($lpad>0) { my $spad = ('-') x $lpad; $s.=$spad; }
    #   substr($s,$qb-1,$ilen)= $sseq; $pairseq{$qid}{$sid}= $s;
    #   print "# $sid=$s\n" if($debug>1);

    # } elsif(/^\W/){ $inq=0;  
    # } elsif(/^Query= (\S+)/){ $qna=$1; 
     
  }
  
  # FIX IDS here: 
  my $pairh= \%pairseq;
  # my(%psfix);
  # for my $id (keys %pairseq) { 
  #  (my $ifix=$id) =~ s/^S/Q/; $ifix= $qna{$ifix}||$id; 
  #  for my $jd (keys %{$pairseq{$id}}) {
  #    (my $jfix=$jd) =~ s/^S/Q/; $jfix= $qna{$jfix}||$jd; 
  #    $psfix{$ifix}{$jfix}= $pairseq{$id}{$jd};
  #  }
  # $pairh= \%psfix;
  # }
  
  my $nids= scalar(keys %$pairh);
  print "#read_exonerate($iname) nids=$nids, npairs=$npairs, ncaln=$nalnc\n" if($debug);
  return ($nids, $pairh);
} 

=item add read_blast0f : pair aligns not mult aln

  .. mix of read_blast3f x read_exonerate..
  ** how to deal w/ un-aligned pair parts? gaps vs 1..3 indels ?
  .. maybe should count for align total?
  
  Note: AT2G35965t1 x AT2G35970t1, AT4G09590t1
     AT2g970 has no mismatches, but gaps/unaligned .. idmax =~ 200b / (290+138 aln) or? /453 total len (shortest)
        a1. Identities = 277/290 (96%), Gaps = 13/290 (4%)
        a2. Identities = 138/138 (100%), Gaps = 0/138 (0%)
     AT4g590 is similar align, adds 4 mismatches.  .. idmax =~ 120b / (290+138 aln) or? /453 total len (shortest)
        a1. Identities = 275/290 (95%), Gaps = 13/290 (4%)
        a2. Identities = 136/138 (99%), Gaps = 0/138 (0%)

=cut

sub read_blast0fmt { 
  my($inh, $iname)=@_;
  my($inq, $qid,$qb,$qseq,$qe,$sid,$sb,$sseq,$se,$slen,$qlen, $bid, $bal, $bgap)= (0) x 19;
  my($nids,$nalnc,$npairs,%pairseq,%pairbe)= (0,0,0); # %qna, 
  print "#read_blast0fmt($iname)\n" if($debug);
  #no: $BASEOK{'.'} = 1;
  #NOTE: blastn missing id x jd align due to low ident.. handle how?
  
  while(<$inh>) {

    if(m/^Query=\s*(\S+)/) { 
      $qid=$1; $qseq=""; $inq=1;  
    
    } elsif(/^Lambda/) { # end qblock, or /^Effective search space/
      ($inq, $qid,$qb,$qseq,$qe,$sid,$sb,$sseq,$se,$slen,$qlen, $bid, $bal, $bgap)= (0) x 19;
      
    } elsif($inq>=1 and m/^>\s*(\S+)/){  # inq++ here?
      $sid=$1; $sseq="";  $inq=2; 
      if(exists $pairseq{$qid}{$sid}{2}) {
        # 2+ align, just append seqs?
      } else {
        $qb=$qe=0; $sb=$se=0;  $npairs++; # $nids++;
        #$pairseq{$qid}{$sid}{1}="n"; #dbg 
        #$pairseq{$qid}{$sid}{2}="n"; #dbg 
      }
      
    } elsif($inq==1 and /^Length=(\d+)/){ 
      $qlen=$1; 
    } elsif($inq==2 and /^Length=(\d+)/){ 
      $slen=$1; 
      # $pairseq{$qid}{$sid}{slen}=$slen; 
      # $pairseq{$qid}{$sid}{qlen}=$qlen;
      
    } elsif($inq>=2 and /^\s*Identities=(\d+).(\d+)/){ 
      ($bid,$bal)=($1,$2); # use bal instead of sum aln? need to add parts
      $bgap= (m/Gaps = (\d+)/)? $1 : 0;
      # $pairseq{$qid}{$sid}{bid}+=$bid; 
      # $pairseq{$qid}{$sid}{bal}+=$bal; 
      # $pairseq{$qid}{$sid}{bgap}+=$bgap; 
      
=item add this  before Query/Subj aln lines
Length=294

 Score = 544 bits (294),  Expect = 9e-159
 Identities = 294/294 (100%), Gaps = 0/294 (0%)
 Strand=Plus/Plus

Query  198  ATTGTTATCTGAAACTTTGAGG-------------CTGCGAGAGGTTAAAGGCGTAGACG  244
            ||||||||||||||||||||||             |||||||||||||||||||||||||
Sbjct  230  ATTGTTATCTGAAACTTTGAGGTGAGGAGGTTAGGCTGCGAGAGGTTAAAGGCGTAGACG  171

Query  245  GTGGTGTCTTGGAGGATGAATTCTGGTTTTTTCGGTTGGAGGATTATCGAAACGAGGAAG  304
            |||||||||||||||||||||||||| ||||||||||||||||||||| |||||||||||
Sbjct  170  GTGGTGTCTTGGAGGATGAATTCTGGGTTTTTCGGTTGGAGGATTATCCAAACGAGGAAG  111

=cut
    
    # } elsif($inq >= 2 and /^(Query|Sbjct)\s+(\d+)\s+(\S+)\s+(\d+)/) {
    } elsif($inq >= 2 and /^(Query|Sbjct)/) {
      my($qs,$ab,$as,$ae)=split" ",$_; #($1,$2,$3,$4); # NOTE rev aln == $ab > $ae
      $as=uc($as);  my $al=length($as);  # my $ae=$ab + $al - 1;
      # if($ab > $ae) { } #? $pairseq{$qid}{$sid}{rev}=1 .. orient below
      
      if($qs eq 'Query') { ## *($inq % 2 == 0)
        if($qb<1){ $qb=$ab; } $qe=$ae; $qseq.= $as;
        $pairseq{$qid}{$sid}{1} .= $as;  
        # $pairseq{$qid}{$sid}{span1}="$qb $qe";
        # $pairbe{$qid}{$sid}{qb}=$qb;  $pairbe{$qid}{$sid}{qe}=$qe; # rev ok?
      } else {   
        if($sb<1){ $sb=$ab; } $se=$ae; $sseq.= $as; $nalnc+= $al;
        $pairseq{$qid}{$sid}{2} .= $as; 
        if($sb > $se) { $pairseq{$qid}{$sid}{orient}= -1; } # see read_axt pairseq orient
        # $pairseq{$qid}{$sid}{span2}="$sb $se";
        # $pairbe{$qid}{$sid}{sb}=$sb;  $pairbe{$qid}{$sid}{se}=$se; 
      } 
      # $inq++; #? drop
    }      

  }

  my $pairh= \%pairseq;
  
  # FIX IDS here: 
  # my(%psfix);
  # for my $id (keys %pairseq) { 
  #  (my $ifix=$id) =~ s/^S/Q/; $ifix= $qna{$ifix}||$id; 
  #  for my $jd (keys %{$pairseq{$id}}) {
  #    (my $jfix=$jd) =~ s/^S/Q/; $jfix= $qna{$jfix}||$jd; 
  #    $psfix{$ifix}{$jfix}= $pairseq{$id}{$jd};
  #  }
  # $pairh= \%psfix;
  # }
  
  my $nids= scalar(keys %$pairh);
  print "#read_blast0fmt($iname) nids=$nids, npairs=$npairs, ncaln=$nalnc\n" if($debug);
  return ($nids, $pairh);
}

=item  read_blast0f notes    
  seq=tmps4d/EVGm019942.cds;  blastn -evalue 1e-9 -query $seq -subject  $seq

Query= AT2G35965t1 type=CDS; aalen=150,65%,complete; clen=696; strand=+;
..
Length=453
                                                                      Score     E
Sequences producing significant alignments:                          (Bits)  Value
AT2G35965t1 type=CDS; aalen=150,65%,complete; clen=696; strand=+;...  837     0.0   
AT2G35970t1 type=CDS; aalen=211,82%,complete; clen=767; strand=+;...  451     9e-131
AT4G09590t1 type=CDS; aalen=211,100%,complete; clen=636; strand=+...  440     2e-127
AT4G09589t1 type=CDS; aalen=97,99%,partial5; clen=295; strand=+; ...  366     3e-105

> AT2G35965t1 type=CDS; aalen=150,65%,complete; clen=696; strand=+; 
.. self-aln

> AT2G35970t1 type=CDS; aalen=211,82%,complete; clen=767; strand=+; 
offs=1-636; codepot=Code/0.013; oid=at18nctair:g818170t1 
Length=636

 Score = 451 bits (244),  Expect = 9e-131
 Identities = 277/290 (96%), Gaps = 13/290 (4%)
 Strand=Plus/Minus

Query  138  GCGTAGGCGTGAAGATGGTCGTAGTAGATTCCGATGTTGGAGTTTCGGTTGCGTGAAGCG  197
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  290  GCGTAGGCGTGAAGATGGTCGTAGTAGATTCCGATGTTGGAGTTTCGGTTGCGTGAAGCG  231

Query  198  ATTGTTATCTGAAACTTTGAGG-------------CTGCGAGAGGTTAAAGGCGTAGACG  244
            ||||||||||||||||||||||             |||||||||||||||||||||||||
Sbjct  230  ATTGTTATCTGAAACTTTGAGGTGAGGAGGTTAGGCTGCGAGAGGTTAAAGGCGTAGACG  171

Query  245  GTGGTGTCTTGGAGGATGAATTCTGGTTTTTTCGGTTGGAGGATTATCGAAACGAGGAAG  304
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  170  GTGGTGTCTTGGAGGATGAATTCTGGTTTTTTCGGTTGGAGGATTATCGAAACGAGGAAG  111

Query  305  ATTGTCATCAAGACGATGATGATGAAGCCGATGATTGCTCCACAGATTCTACAGGCGGTT  364
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  110  ATTGTCATCAAGACGATGATGATGAAGCCGATGATTGCTCCACAGATTCTACAGGCGGTT  51

Query  365  CCGCCTCCTCCACCGCCACCACCGTGGTTTCCGCACTCTTTGGTCGTCAT  414
            ||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  50   CCGCCTCCTCCACCGCCACCACCGTGGTTTCCGCACTCTTTGGTCGTCAT  1


 Score = 255 bits (138),  Expect = 8e-72
 Identities = 138/138 (100%), Gaps = 0/138 (0%)
 Strand=Plus/Minus

Query  1    ATGGAGATGATAATTCCCTATAGTAAGAGTCCCGACCTTCCACCTCACGCGTCCATCAAC  60
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  528  ATGGAGATGATAATTCCCTATAGTAAGAGTCCCGACCTTCCACCTCACGCGTCCATCAAC  469

Query  61   GCATATAGTCAACGTGAAAACCCCACTATTCTGCTCGTCCCCGAGAGCCACGGCGTTGAA  120
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  468  GCATATAGTCAACGTGAAAACCCCACTATTCTGCTCGTCCCCGAGAGCCACGGCGTTGAA  409

Query  121  AGGAGCGATAGGGACTTG  138
            ||||||||||||||||||
Sbjct  408  AGGAGCGATAGGGACTTG  391

> AT4G09590t1 type=CDS; aalen=211,100%,complete; clen=636; strand=+; 
offs=1-636; codepot=Code/0.016; oid=at18nctair:g826543t1 
gene=NHL22; geneid=826543; db_xref=Araport:AT4G09590,TAIR:AT4G09590,ncbiprot:NP_192697.1,ncbirna:NM_117027; 
isoform=1; 
Name=NDR1/HIN1-like 22
Length=636

 Score = 440 bits (238),  Expect = 2e-127
 Identities = 275/290 (95%), Gaps = 13/290 (4%)
 Strand=Plus/Minus

Query  138  GCGTAGGCGTGAAGATGGTCGTAGTAGATTCCGATGTTGGAGTTTCGGTTGCGTGAAGCG  197
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  290  GCGTAGGCGTGAAGATGGTCGTAGTAGATTCCGATGTTGGAGTTTCGGTTGCGTGAAGCG  231

Query  198  ATTGTTATCTGAAACTTTGAGG-------------CTGCGAGAGGTTAAAGGCGTAGACG  244
            ||||||||||||||||||||||             |||||||||||||||||||||||||
Sbjct  230  ATTGTTATCTGAAACTTTGAGGTGAGGAGGTTAGGCTGCGAGAGGTTAAAGGCGTAGACG  171

Query  245  GTGGTGTCTTGGAGGATGAATTCTGGTTTTTTCGGTTGGAGGATTATCGAAACGAGGAAG  304
            |||||||||||||||||||||||||| ||||||||||||||||||||| |||||||||||
Sbjct  170  GTGGTGTCTTGGAGGATGAATTCTGGGTTTTTCGGTTGGAGGATTATCCAAACGAGGAAG  111

Query  305  ATTGTCATCAAGACGATGATGATGAAGCCGATGATTGCTCCACAGATTCTACAGGCGGTT  364
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  110  ATTGTCATCAAGACGATGATGATGAAGCCGATGATTGCTCCACAGATTCTACAGGCGGTT  51

Query  365  CCGCCTCCTCCACCGCCACCACCGTGGTTTCCGCACTCTTTGGTCGTCAT  414
            ||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  50   CCGCCTCCTCCACCGCCACCACCGTGGTTTCCGCACTCTTTGGTCGTCAT  1


 Score = 244 bits (132),  Expect = 2e-68
 Identities = 136/138 (99%), Gaps = 0/138 (0%)
 Strand=Plus/Minus

Query  1    ATGGAGATGATAATTCCCTATAGTAAGAGTCCCGACCTTCCACCTCACGCGTCCATCAAC  60
            ||||||||||||||||||||||||||||||||||||||||||||||||  ||||||||||
Sbjct  528  ATGGAGATGATAATTCCCTATAGTAAGAGTCCCGACCTTCCACCTCACTTGTCCATCAAC  469

Query  61   GCATATAGTCAACGTGAAAACCCCACTATTCTGCTCGTCCCCGAGAGCCACGGCGTTGAA  120
            ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sbjct  468  GCATATAGTCAACGTGAAAACCCCACTATTCTGCTCGTCCCCGAGAGCCACGGCGTTGAA  409

Query  121  AGGAGCGATAGGGACTTG  138
            ||||||||||||||||||
Sbjct  408  AGGAGCGATAGGGACTTG  391

exonerate pairalign, 
* note hi 100% idmax AT2G35965t1 x 970t1 , not symmetric (970t1 x 965t1 idmax=51%), 
 13b gap *should* split that to 47% (200b max ident) / (290+138 aln)
grep -v '#' at18tmps4d_evg2dupx8t.vals | grep AT2G35965t1 | egrep 'AT2G35970t1|AT4G09589t1'
ID____  JD____  Ispar   par_score       Naln    Nsnp    S_par   S_idmax S_var   S_ident d3Codon S_peak  S_iCBIN S_MMF   B_par   B_ident C_par   C_cid
AT2G35965t1     AT2G35970t1     alt_i   0       277     0       alt_sn  100     0       100.0   33      s0spot  100.00  99.64alt_ta     100.0   unk_cp  0
AT2G35965t1     AT4G09589t1     alt_i   0       204     2       alt_sn  63.7    7.6     99.0    50      s1spot  20.00   97.55alt_ta     100.0   unk_cp  0
AT2G35970t1     AT2G35965t1     alt_i   0       274     20      alt_sn  51.1    5.0     92.7    25      s1spot  4.00    90.88alt_ta     100.0   unk_cp  0
AT4G09589t1     AT2G35965t1     alt_i   0       204     2       alt_sn  63.7    7.6     99.0    50      s1spot  20.00   97.55alt_ta     100.0   unk_cp  0

muscle multalign
grep -v '#' at18tmps4d_evg2dupx7t.vals | grep AT2G35965t1 | egrep 'AT2G35970t1|AT4G09589t1'
ID____  JD____  Ispar   par_score       Naln    Nsnp    S_par   S_idmax S_var   S_ident d3Codon S_peak  S_iCBIN S_MMF   B_par   B_ident C_par   C_cid
AT2G35965t1     AT2G35970t1     par_i   37.9    292     114     par_sn  4.1     27.9    61.0    35      s3spot  0.78    55.82   par_ta  72.6    par_cp  1
AT2G35965t1     AT4G09589t1     par_i   0       234     3       par_sn  61.1    38.6    98.7    33      s1spot  14.29   97.01   alt_ta  99.4    alt_cp  5
AT2G35970t1     AT2G35965t1     par_i   37.9    292     114     par_sn  4.1     27.9    61.0    35      s3spot  0.78    55.82   par_ta  72.6    par_cp  1
AT4G09589t1     AT2G35965t1     par_i   0       234     3       par_sn  61.1    38.6    98.7    33      s1spot  14.29   97.01   alt_ta  99.4    alt_cp  5
  
=cut

#========= BLASTIN sub ===========

=item BLASTIN fmt3

-subject pt.cds -query pt.cds 
Database: User specified sequence set (Input: EVGm000457.cds).

Query_5    61    CTCCAATCTACCATGCGCGCGATTGAACTTGCTTGCTCTTACATTCAGATTAATAGTAAT  120
Subject_5  61    ............................................................  120
Subject_2  331   ............................................................  390
Subject_3  61    ............................................................  120
Subject_4  70                                           ..T..................  90
Subject_1  94                                        ....T.G.A...............  117
                 ^^^^ reader needs add symbol '-' here, or at blank end.
                 
alt: -db self.cds -query pt.cds 
Database: EVGm000457.cds

Query_3  61    CTCCAATCTACCATGCGCGCGATTGAACTTGCTTGCTCTTACATTCAGATTAATAGTAAT  120
2        61    ............................................................  120
1        331   ............................................................  390
4        61    ............................................................  120
3        70                                           ..T..................  90
0        94                                        ....T.G.A...............  117


=cut

sub read_blast3fmt { # outfmt=3 always?
  my($inh, $iname)=@_;
  my($qid,$qb,$qseq,$qe,$sid,$sb,$sseq,$se,$inq, $qna, $isq,$ilen)= (0) x 9;
  $BASEOK{'.'} = 1;
  my(%qna, %pairseq);
  print "#read_blast3fmt($iname)\n" if($debug);

  while(<$inh>) {
    # fixme Subj: add '-' in seq blanks
    if(/^(Query\w+)/){ ($qid,$qb,$qseq,$qe)=split" ",$_; $qid=~s/Query_/Q/; 
      $qna{$qid}=$qna if($qna); $inq=$qid; $qna=0; $qseq=uc($qseq);
      $pairseq{$qid}{$qid} .= $qseq;  $isq= index($_,$qseq); $ilen=length($qseq); 
      print "# $qid:$pairseq{$qid}{$qid}\n" if($debug>1);
      
    } elsif($inq and /^(Subject|\w)/) { 
      ($sid,$sb,$sseq,$se)=split" ",$_;  $sid=~s/Subject_/S/;
      $sseq= substr($_,$isq,$ilen); $sseq =~ s/ /\-/g;  $sseq=uc($sseq);
      # print "$sid:$sseq\n" if($debug>1);
      # BUG here, sseq dont all start at same qb-base
      # $pairseq{$qid}{$sid} .= $sseq; 
      
      my $s=$pairseq{$qid}{$sid}; 
      my $sl=length($s); my $lpad= $qb + $ilen -1 - $sl; 
      if($lpad>0) { my $spad = ('-') x $lpad; $s.=$spad; }
      substr($s,$qb-1,$ilen)= $sseq; $pairseq{$qid}{$sid}= $s;
      print "# $sid=$s\n" if($debug>1);

    } elsif(/^\W/){ $inq=0;  
    } elsif(/^Query= (\S+)/){ $qna=$1; 
    }
  }
  
  # FIX IDS here: 
  my $pairh= \%pairseq;
  my(%psfix);
  for my $id (keys %pairseq) { 
   (my $ifix=$id) =~ s/^S/Q/; $ifix= $qna{$ifix}||$id; 
   for my $jd (keys %{$pairseq{$id}}) {
     (my $jfix=$jd) =~ s/^S/Q/; $jfix= $qna{$jfix}||$jd; 
     $psfix{$ifix}{$jfix}= $pairseq{$id}{$jd};
   }
  $pairh= \%psfix;
  }
  
  my $nids= scalar(keys %$pairh);
  return ($nids, $pairh);
} 

# sub blpairs { # drop, pairs() now ok
#   my(%pairseq)=@_;
#   my @ids=sort keys %pairseq; my $ns=@ids; 
#   for (my $i=0; $i<$ns; $i++) { 
#     my $idi=$ids[$i];  my $si=$pairseq{$idi}{$idi};
#     my @jds= sort keys %{$pairseq{$idi}};
#     for my $idj (@jds) { next if($idj eq $idi);
#       my $sj=$pairseq{$idi}{$idj}; 
#       #? move BLASTIN '.' fix here? change sj[k]==. to si[k]
#       # $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
#       my (@se,@hd);
#       # fixed: if($TYDIST==2)  
#       @se= snp_distke($idi,$si,$idj,$sj); @hd= snp_distke('HD') if(1 > $hdr); 
#       # else { @se=snp_diste($idi,$si,$idj,$sj); @hd= snp_diste('HD') if(1 > $hdr); }
#       print join("\t",qw(IDi IDj),@hd),"\n" if(1 > $hdr++); 
#       my($idin,$idjn)= map{ (my $d=$_) =~ s/^S/Q/; $qna{$d}||$_ } ($idi,$idj);
#       print join("\t",$idin,$idjn,@se),"\n"; 
#     }
#   } 
# } 

#=========== BLASTIN.end ==================

__END__

# sub snp_diste {  
#   my($id,$si,$jd,$sj)=@_; 
#   
#   my $doKURT=0; my $doD=2;
#   my @hd=qw(Nc Nd pDsn aDsn vDsn mDsn Dspan Ne aEsn vEsn mEsn Espan);
#   push @hd, "Nz", "zKurt$doKURT", "zrange" if($doKURT);
#   if($doD==2) { map{ s/D/D2/; } @hd; }
#   return(@hd) if($id eq 'HD');
#   
#   my $ni=length($si); my $nj=length($sj); my $ns=($ni>$nj)?$nj:$ni; 
#   my($ldk,$nc,$eq,$nd,$ds,$dss, $es, $ess,$ne,)=(0) x 19;
#   my(@ds,@dk,@es,@ek,@sii,@sjj);
#   my @si=split"",$si; my @sj=split"",$sj;  
#   for(my $k=0; $k<$ns; $k++){ 
#     my $bi=$si[$k]; my $bj=$sj[$k]; 
#     $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
#     if($BASEOK{$bi} and $BASEOK{$bj}){ push @sii,$bi; push @sjj,$bj; } 
#   }
#   $ns= @sii; @si=@sii; @sj=@sjj; # note: $ns == final $nc here
#   
#   for(my $k=0; $k<$ns; $k++){ 
#     my $bi=$si[$k]; my $bj=$sj[$k];
#     $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
#      
#     if(1){ # if($bi=~/[ACGT]/i and $bj=~/[ACGT]/i)
#       my $c= $nc++; # here: $c == $k;
#       if($bi ne $bj) { 
#         if($doD==2) {
#         my $d= int(1000*$c/$ns)/10; $ds+=$d; $dss+=$d*$d; push @ds,$d; 
#         } else {
#         if($ldk) { my $d=$c - $ldk; $ds+=$d; $dss+=$d*$d; push @ds,$d; }  
#         }
#         if($ldk and $si[$k-1] eq $sj[$k-1] and ($k==$ns-1 or $si[$k+1] eq $sj[$k+1])) { 
#           my $d=$c - $ldk; $es+=$d; $ess+=$d*$d; push @es,$d; push @ek,$c;  $ne++; 
#           }  
#         if($doD==2) { push @dk, int(1000*$c/$ns)/10; } 
#         else {  push @dk,$c; }
#         $nd++; $ldk=$c;
#       } 
#       # else { $eq++; }
#     } 
#   }
#   my ($dzs,$nz,$kurt,$kurtvals,@dzs)=(0,0,0,0);
#   if($doKURT) {
#   # kurtosis calc from dz = (di - da)/dsd ; kurt= sum(dz^4)/n - 3
#   # d == snp-neighbor dist, try also c/nc == %span
#   $ldk=0;
#   my($da,$dsd);
#   if($doKURT==2) {
#     my($ds,$dss)=(0,0); 
#     for my $c (@dk) { my $d; if($doD==2) { $d=$c; } else { $d=100*$c/$nc; }  $ds+=$d; $dss+=$d*$d; }
#     $da= ($nd<1)?0: $ds/$nd;  $dsd= ($nd<2)?1: sqrt( ($dss - $ds*$ds/$nd)/($nd));
#   } else {
#     $da= ($nd<1)?0: $ds/$nd;  $dsd= ($nd<2)?1: sqrt( ($dss - $ds*$ds/$nd)/($nd));
#   }
#   for(my $k=0; $k<$ns; $k++){ 
#     my $bi=$si[$k]; my $bj=$sj[$k]; 
#     $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
#     my $c=$k; 
#     if($bi ne $bj) { 
#       if($doKURT==2) {
#       my $d=100*$c/$nc;  my $dz= ($d - $da)/$dsd;  $dzs += $dz**4; $nz++; push @dzs,$dz;
#       } else {
#       if($ldk) { my $d=$c - $ldk; my $dz= ($d - $da)/$dsd; $dzs += $dz**4; $nz++; push @dzs,$dz; }  
#       $ldk=$c;
#       }   
#       } 
#     } 
#     if($nz>0) {
#     $kurt= sprintf "%.3g", $dzs/$nz - 3; 
#     my @m=(0,int($nz/4),int($nz/2),int(3*$nz/4),-1);
#     $kurtvals= join ",", map{ sprintf "%.2f",$_ } @dzs[@m];
#     }
#   }
#   
#   my @sds=sort { $a<=>$b } @ds;
#   my $da= ($nd<1)?0: sprintf("%.1f",$ds/$nd); 
#   my $dsd= ($nd<2)?0: sprintf "%.1f", sqrt( ($dss - $ds*$ds/$nd)/($nd-1));
#   my $m= int($nd/2); my $dm= ($nd<1)?0:$sds[$m]; 
#   my $pd= ($nc<1)?0: sprintf("%.3g",$nd/$nc); 
#   my $dks= ($nd<1)?"0,0,0":join(",",@dk[0,$m,-1]); # this, snp-span / total span (nc) may be useful
#   
#   my $ea= ($ne<1)?0: sprintf("%.1f",$es/$ne); my @ses=sort { $a<=>$b } @es;
#   my $esd= ($ne<2)?0: sprintf "%.1f", sqrt( ($ess - $es*$es/$ne)/($ne-1));
#   my $m= int($ne/2); my $em= ($ne<1)?0:$ses[$m]; 
#   my $eks= ($ne<1)?"0,0,0":join(",",@ek[0,$m,-1]); # this, snp-span / total span (nc) may be useful
#   
#     # alts have short snp-span, pars long span .. count of difs, nd / nc about same as global 1 - sim
#     # a couple cases of alt/par w/ similar nd, alts have shorter snp-span / longer align span (nc), pars snp-span ~= align-span
#   my @ret=($nc,$nd,$pd,$da,$dsd,$dm,$dks,  $ne,$ea,$esd,$em,$eks);
#   push @ret, $nz, $kurt, $kurtvals if($doKURT);
#   return @ret; 
# } 


# sub snp_dist {  
#   my($id,$si,$jd,$sj)=@_; 
#   return(qw(Nc Nd pDsn aDsn sdDsn mDsn Dspan)) if($id eq 'HD');
#   my $ni=length($si); my $nj=length($sj); my $ns=($ni>$nj)?$nj:$ni; 
#   my($ldk,$nc,$ne,$nd,$ds,$dss)=(0) x 9;
#   my(@ds,@dk);
#   my @si=split"",$si; my @sj=split"",$sj;  
#   for(my $k=0; $k<$ns; $k++){ 
#     my $bi=$si[$k]; my $bj=$sj[$k]; 
#     $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
#     my $ic=($k % 3); 
#     if($bi=~/[ACGT]/i and $bj=~/[ACGT]/i){ my $c= $nc++; 
#       if($bi ne $bj) { if($ldk) { my $d=$c - $ldk; $ds+=$d; $dss+=$d*$d; push @ds,$d; }  push @dk,$c; $nd++; $ldk=$c;} 
#       else { $ne++; }
#     } 
#   }
#   my @sds=sort { $a<=>$b } @ds;
#   my $da= ($nd<1)?0: sprintf("%.1f",$ds/$nd); 
#   my $dsd= ($nd<2)?0: sprintf "%.1f", sqrt( ($dss - $ds*$ds/$nd)/($nd-1));
#   my $m= int($nd/2); my $dm= ($nd<1)?0:$sds[$m]; 
#   my $pd= ($nc<1)?0: sprintf("%.3g",$nd/$nc); 
#   my $dks= ($nd<1)?"0,0,0":join(",",@dk[0,$m,-1]); # this, snp-span / total span (nc) may be useful
#     # alts have short snp-span, pars long span .. count of difs, nd / nc about same as global 1 - sim
#     # a couple cases of alt/par w/ similar nd, alts have shorter snp-span / longer align span (nc), pars snp-span ~= align-span
#   return($nc,$nd,$pd,$da,$dsd,$dm,$dks); 
# } 
# 
# sub kaks { # basic kaks == cmpp
#   my($id,$si,$jd,$sj)=@_; 
#   return(qw(Nsd pSYN bS pNS bN kaks)) if($id eq 'HD');
#   my $ni=length($si); my $nj=length($sj); my $ns=($ni>$nj)?$nj:$ni; 
#   my ($nc,%ne); my @si=split"",$si; my @sj=split"",$sj;  
#   for(my $k=0; $k<$ns; $k++){ 
#     my $bi=$si[$k]; my $bj=$sj[$k]; 
#     $bj=$bi if($BLASTIN and $bj eq '.'); #BLASTIN fix, depends on si,sj order
#     my $c=($k % 3); 
#     if($bi=~/[ACGT]/i and $bj=~/[ACGT]/i){ $nc++; if($bi ne $bj) { $ne{$c}++; } } 
#     } 
#   my $ss=int(0.5 + ($ne{0}+$ne{1})/2); my $sn=$ne{2}||0; 
#   my($ps,$pn)=map{ ($nc<1)?0:sprintf"%.3g",$_/$nc } ($ss,$sn); 
#   my $kas=($ss<1)?0:sprintf"%.2f",$sn/$ss; 
#   return($nc,$ps,$ss,$pn,$sn,$kas); 
# } 


=item  at18snptest.info

./altparsnpcb.pl tmpshi3c/EVGm000023.msa
#in=tmpshi3c/EVGm000023.msa
IDi_____        IDj_____        Nc      Nd      pD2sn   aD2sn   vD2sn   mD2sn   D2span  Ne      aEsn    vEsn    mEsn    Espan
AT1G64790t1     AT1G64790t2     7929    17      0.00214 0.2     0.2     0.1     0,0.1,0.6       6       5.2     4.0     6       28,44,53
AT1G64790t1     AT1G64795t1     201     38      0.189   44.7    26.9    48.2    1.4,48.2,93     25      5.4     3.7     4       5,97,187
AT1G64790t2     AT1G64795t1     201     38      0.189   44.7    26.9    48.2    1.4,48.2,93     25      5.4     3.7     4       5,97,187

# using snp_diste ... aD2sn, var, medn are stats of snp-locs as location on align-span (100*position/Nc)
# aEsn, v, m Espan are snpi - snp(i-1) distances, for snp(i) not adjacent.  D2 var (sd) has some relation to alt(low)/par(hi var)
# Nc = aligned bases, Nd = snp count, pD2sn = snp count/Nc, a,v,m,span = stats for D2 (100*position/Nc)

#in=tmpshi3c/EVGm000088.msa
IDi_____        IDj_____        Nc      Nd      pD2sn   aD2sn   vD2sn   mD2sn   D2span  Ne      aEsn    vEsn    mEsn    Espan
AT3G24870t1     AT3G24870t2     5690    18      0.00316 2.7     0.2     2.8     2.4,2.8,3       1       2.0     0       2       175,175,175
AT3G24870t1     AT3G24870t3     4190    22      0.00525 21.2    37.9    3.8     3.3,3.8,99.9    5       803.0   1784.4  6       175,4175,4188
AT3G24870t1     AT3G24870t4     5696    18      0.00316 2.7     0.2     2.8     2.4,2.8,3       1       2.0     0       2       175,175,175
AT3G24870t1     AT3G24880t2     5696    58      0.0102  47.0    34.8    48.7    2.4,48.7,95.6   41      128.7   265.5   48      175,4047,5448
AT3G24870t1     AT3G24880t3     5696    58      0.0102  47.0    34.8    48.7    2.4,48.7,95.6   41      128.7   265.5   48      175,4047,5448
...

#in=tmpshi3c/EVGm000128.msa
IDi_____        IDj_____        Nc      Nd      pD2sn   aD2sn   vD2sn   mD2sn   D2span  Ne      aEsn    vEsn    mEsn    Espan
AT1G21650t1     AT1G21650t2     3177    51      0.0161  2.9     1.8     2.9     0.1,2.9,6.4     23      5.0     3.6     4       7,88,206
AT1G21650t1     AT1G21650t3     3156    51      0.0162  2.9     1.8     2.9     0.1,2.9,6.5     23      5.0     3.6     4       7,88,206
AT1G21650t1     AT1G21651t1     2426    422     0.174   74.5    14.8    74.4    48.6,74.4,99.7  189     3.8     2.2     3       1188,1803,2420
AT1G21650t2     AT1G21650t3     3156    0       0       0       0       0       0,0,0   0       0       0       0       0,0,0
AT1G21650t2     AT1G21651t1     1444    472     0.327   51.8    28.3    52.6    0.3,52.6,99.5   212     3.9     2.3     3       7,763,1438
AT1G21650t3     AT1G21651t1     1444    472     0.327   51.8    28.3    52.6    0.3,52.6,99.5   212     3.9     2.3     3       7,763,1438

#in=tmpshi3c/EVGm000457.msa
IDi_____        IDj_____        Nc      Nd      pD2sn   aD2sn   vD2sn   mD2sn   D2span  Ne      aEsn    vEsn    mEsn    Espan
AT3G04490t1     AT3G04490t2     3486    0       0       0       0       0       0,0,0   0       0       0       0       0,0,0
AT3G04490t1     AT3G04490t4     3357    0       0       0       0       0       0,0,0   0       0       0       0       0,0,0
AT3G04490t1     AT4G38092t1     393     44      0.112   16.8    17.6    13.2    1,13.2,86.7     22      13.5    31.8    2       7,55,341
AT3G04490t1     AT4G38092t2     366     33      0.0902  16.2    19.0    10.9    2.1,10.9,85.7   9       29.3    47.7    6       16,68,314
AT3G04490t2     AT3G04490t4     3342    0       0       0       0       0       0,0,0   0       0       0       0       0,0,0
AT3G04490t2     AT4G38092t1     351     25      0.0712  16.5    21.5    11.6    0,11.6,85.1     13      21.2    40.1    4       6,45,299
AT3G04490t2     AT4G38092t2     326     17      0.0521  16.4    25.7    5.8     0,5.8,84        6       42.3    55.0    47      8,176,274
AT3G04490t4     AT4G38092t1     351     25      0.0712  16.5    21.5    11.6    0,11.6,85.1     13      21.2    40.1    4       6,45,299
AT3G04490t4     AT4G38092t2     326     17      0.0521  16.4    25.7    5.8     0,5.8,84        6       42.3    55.0    47      8,176,274
AT4G38092t1     AT4G38092t2     355     31      0.0873  8.2     5.4     7.8     0,7.8,17.7      12      2.8     1.1     2       7,41,63

=cut
