#!/usr/bin/perl
# gnodes_sam2covtab23jul.pl TEST2 of gnodes_sam2covtab22aug.pl TEST

use strict;
use Getopt::Long; # replace ENV w/ -opts

use constant UPD22MAY => 1; # see above item, test
use constant UPD22JUL => 1; #?same as UPD22MAY?
use constant UPD22AUG => 1; 
use constant UPD23JUL => 1; 
use constant NOHARDCLIP =>  UPD22JUL; # option or not? need tests?

# use constant PUT_covp_in_allcov => UPD22AUG; # UPD22AUG Needs TESTING ..  new all-bases column, replace aCovM or not?
my $PUT_covp_in_allcov = UPD22AUG; # UPD22AUG Needs TESTING ..  new all-bases column, replace aCovM or not?
my $TEST_UNBASES= UPD22MAY; # count unmapped bases of mapped reads, ie cigar H/S/other?, not same as readlen; see addCigar..
my $ADD_I_TO_ALEN = $ENV{addItoAlen} || 0; #UPD22AUG
my $ALLOW_SOFTCLIP = $ENV{softclip}|| 0; #  

use constant UPD22AUGfinal => 1;
if(UPD22AUGfinal) {
  $PUT_covp_in_allcov=0;
  $TEST_UNBASES=0;
  $ADD_I_TO_ALEN=1;
  $ALLOW_SOFTCLIP=1;
}

=item UPD22AUG: final settings?  
 
  covtab cols: 
    Crid Crb CovCDS CovTE CovUNK  aCovT aCovM aCovU aCovP
  drop: CovPA, CovPT : TEST_UNBASES=0, revert to CovTE, CovUNK 
  keep: aCovM as was (M align sum), PUT_covp_in_allcov = 0
  add : aCovP == M+S+I, ie all read bases around align locus, though I, S ambiguous placement
  PUT_covp_in_allcov => 0; TEST_UNBASES = 0;
  
  ADD_I_TO_ALEN Needs test: for longrd,and short,  maybe add I to alen, loc{td}[cend]{tun,N}, but not S (outside of geneseq)

  UPD22AUG: reuse option -[no]softclip w/ TEST_UNBASES, now unused to count/notcount Sclip in aCovP
  env addItoAlen=1 softclip=1 means aCovP= M+I+S
  env addItoAlen=0 softclip=0 means aCovP= M == aCovM
  
=cut
  
my $VERS='9n'; # UPD22AUG

use constant { SAMFLAG_paired => 0x01, SAMFLAG_nomap => 0x04, SAMFLAG_munmap => 0x08, SAMFLAG_rev => 0x10, 
               SAMFLAG_read2 => 0x80, SAMFLAG_2ndary => 0x100, SAMFLAG_suppl => 0x800 }; 
use constant NSAMPLE => 50000; # 20000; ## 500 not enough when variable like Fig.MiSeq
use constant MIN_MINALN => 30; ## require this min align
use constant MAX_MINALNCDS => 120; ## max for shorter cds aligns, w/ intron breaks
use constant kMAXIDCLASS => 9; # idclass limit, using 3-4 now
use constant GENECOVBINS => 5; # 3 == 8c 8b upd for genescov      
use constant CDSRETREF => 1;    
use constant TRY_GENELOC => 2; 

my $debug=$ENV{debug}||0; # test
my $MIN_IDENT= 0.40; # UPD7e was 0.65; # lo is best?
my $MIN_DUPIDENT = 0.98; # 7f= 0.999; #UPD7e was 0.98; # was .99/1; hi is best? or 1.0; # == ident equal to 1st/top align, lower if desired
my $BN=$ENV{binsize}||100; 
my $CRTPAT=''; # no default, this is bad: '^[A-Za-z0-9]+'; #UPD7? TE|CDS|UNK chr class tag for SAVE_RDIDS
my $SKIPR=$ENV{skipread}||0; # FIXME: skipread=0 means ignore, readmap.bam not paired

my($icpu,$ncpu,$outtab,$inbam,$cdsbam,$inchrtab,$duptab,$topcount,$crclassf,$samcpu,$cdstab,$rdidtab)=(0) x 19;
  #obsolete: $rdidtab= undef;
my( $MINALN, $nsample, $readlen) = (0) x 9; #UPD22MAY
my @sreadlen; # $sminaln, $sreadlen, 

# UPD22AUG: these are covtab outputs as #comm totals, used by covsum
# UPD22AUG: N_ align, insert, delete, softclip, mismat are now base counts, not read counts
my( $sum_readlen, $sum_unmap, $sum_supplen, $sum_duplen, $sum_rdalign, $sum_rdalen)= (0) x 9; 
my( $n_readid, $n_nomap, $n_notcr, $n_mapbad, $n_dupbad, $n_mapok, $n_partb,
   $n_intron, $n_align, $n_insert, $n_delete, $n_softclip, $n_mismatch)= (0) x 19; 
my( $n1intron, $n1align, $n1insert, $n1delete, $n1softclip, $n1mismatch)= (0) x 9; # base counts for primary align only
   
my($topaln,$lid,$idp,$idn,$ndi,$ncdsrd)=(0) x 9;
my $MERGE = undef; # defined($MERGE) now means do it
my ($RIDPREFIX,$GIDPREFIX,$ncutGIDPRE,$RDIDIsNum,$RDIDIsNumerr)=(0) x 9;  #UPD7e need to test each read set?
  # $RIDPREFIX obsolete v8i; $RDIDIsNum,$RDIDIsNumerr obsolete v8f

my $outgenetab = undef; my $only_genetab=0; # UPD21JUN option
my $SAVE_GENECOV= 0;  # now == hasCdsBam
my $USE_CDSBAMCLASS=1; # UPD21DEC, maybe make default, depends on idclass table values (CDS,TE,UNK)
my $ALN_SUB_NMI = 0; # obsolete: (defined $ENV{nonmi}) ? 0 : (UPD22MAY)? 0 : 1; # UPD21NOV: TEST for hetero adjust, dont subtract NM:i:nn  from align val
# UPD22MAY: turn ALN_SUB_NMI default off, want to count all M, even mismatch, as aligned

my $optok= GetOptions( 
  'output|covtab=s',\$outtab, 
  'bam=s', \$inbam, 
  'cdsbam=s', \$cdsbam, 
  'chrtab=s',\$inchrtab, #<< change to $inchrtab to avoid fname confusion, outchrtab differs, always $outtab.chrtab
  'ridprefix=s',\$RIDPREFIX, 'gidprefix=s',\$GIDPREFIX,
  'genetab:s',\$outgenetab, # write table of genes coverage, using cds-readids x chr read map cov
  'onlygenetab!',\$only_genetab,
  'CRTPAT=s',\$CRTPAT, # use w/ savereadids
  'crclassf|idclassf=s',\$crclassf, # alt table chr => class for savereadids
  'icpu=i', \$icpu, 'ncpu=i', \$ncpu,  'samcpu=i',$samcpu,
  'topcount=i', \$topcount, # quick sample, samview bam | top -n 10_000_000
  'minident=s',\$MIN_IDENT, 'mindupident=s', \$MIN_DUPIDENT,  
  'minalign|MINALN=s',\$MINALN,  
  # 'MAXSHORTRD=s',\$MAXSHORTRD,  # UPD22MAY, test turn off bad longrd for true longrd 
  # 'longreads!',\$LONGR,
  # 'nmi!',\$ALN_SUB_NMI, # -nonmi turns off
  'softclip!',\$ALLOW_SOFTCLIP,
  'usecdsclass!',\$USE_CDSBAMCLASS, # requires -idclass/crclassf and hasCdsBam
  'binsize=i',\$BN, 'skipread=i',\$SKIPR, 
  'merge:s',\$MERGE, # UPD7, flag to merge ncpu part tables : 'merge!',
  'debug!', \$debug, 
  );

my $optinok= (($inbam and -s $inbam) or (defined $MERGE and ($outtab or @ARGV)) );

die "usage: sam2covtab -bam name.chr_reads.bam -cdsbam name.cdste.bam -out name.v$VERS.covtab
  requires 'samtools view inbam'
  opts: -minident=$MIN_IDENT -mindupident=$MIN_DUPIDENT -minalign=$MINALN 
  -ncpu $ncpu -icpu 0..3  : subsets for parallel process, then -merge subsets
  -binsize=$BN  -skipread=2|1 (which of paired reads)  
  -idclass genes.classtab :  
sam2covtab -merge -out name  : merge all table parts from icpu 0..3,  
sam2covtab -merge=chrtab -out name.chrtab part1.chrtab part2.chrtab  : merge one table parts from ARGS
"  unless($optok and $optinok);

# readChrtab() globals
my ($NOK,$haslen,%crlen,%crdtype)=(0,0,0); #8e.drop: %crok, $hasread,,%cread 
my ($NCDS,$hasCdsBam,$useRIDp)=(0,0,0);  
my @cdsrdclass_in=(0) x 5; my @cdsrdclass_sum=(0) x 5; my @cdsrdclass_miss=(0) x 5; 

our( %crmax, # chr max loc found in chr.bam; see also %crlen should be ~same
  %cov, %covt, %covu, # UPD21AUG20 how are these used now?
  %allcov, %allcovt, %allcovu, # primary chr cov data
  %allcovp, #UPD22AUG
  %covgene, %covgenebin, # UPD21AUG20 change struct
  %geneindex, # indices from gene id/size table
     @agenesize, # replace  %cdslen
     @agenebins, @ageneclass, #UPD21DEC class  
  @agenecov, @agenebincov, @agenecrloc, # cover counts from bams
);

my($nidclass,$idclassh,$idclasslist)=(0); # read_idclass globalse; renamed crclass
my($MINALNCDS, $lcdsreadid, $cdsiid, $lastcdsrow) = (0) x 4; # getCdsRead globals, was our()
$MINALNCDS= $MINALN;

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

sub MAIN_stub {}

  $ncpu||=1;  $icpu=0 if($ncpu==1);
  $MIN_IDENT /= 100 if($MIN_IDENT > 1);
  $MIN_DUPIDENT /= 100 if($MIN_DUPIDENT > 1);
  
  my $iname=$inbam || $outtab; $iname =~ s/\.(bam|covtab|chrtab|genetab|readids)//;
  $outtab= "$iname.v$VERS.covtab" unless($outtab); #? $iname.$VERS.covtab
  my $outchrtab= $outtab;  $outchrtab=~s/\.\w+$//; $outchrtab.=".chrtab";
 
  $hasCdsBam= ($cdsbam and -s $cdsbam)?1:0;
  if($hasCdsBam or defined $outgenetab) { 
    $SAVE_GENECOV=  1;
    unless($outgenetab =~ m/\w\w/) {  $outgenetab= $outtab; $outgenetab=~s/\.\w+$//; $outgenetab.=".genetab"; }    
  }
  
  my $outpart= $outtab;
  my $outchrpart= $outchrtab; if($outchrpart eq $inchrtab) { $outchrpart.="out"; } # name bug: .chrtabout with -merge
  my $outgenepart= $outgenetab; # may be undef

  if(defined $MERGE) { 
    # -merge : merge all table type, or -merge=covtab,chrtab,readids,.. which type only
    unless($MERGE=~/\w/){ $MERGE=($only_genetab)?"genetab":"covtab,chrtab,genetab,readids"; }# all types
    if($MERGE=~/gene/ and not $outgenetab =~ m/\w/) { # fix if genetab.pt* but no -genetab or -cdsbam flag
       $outgenetab= $outtab; $outgenetab=~s/\.\w+$//; $outgenetab.=".genetab"; 
       #? or fail: unless(-s "$outgenetab.pt1") { $MERGE=~s/genetab//; }
    } 
    
    mergeparts("covtab", $outtab, @ARGV) if($MERGE=~/cov/); 
    mergeparts("chrtab", $outchrtab, @ARGV) if($MERGE=~/chr/); # -merge assume chrtab=output name, was outchrpart => .chrtabout bug
    mergeparts("genetab", $outgenetab, @ARGV) if($MERGE=~/gene/);   
    #o: mergeparts("readids", $rdidtab || "$iname.readids", @ARGV) if($MERGE=~/readid/);    
    exit;
  }
    

  my $topline= "#sam2covtab par: minident=$MIN_IDENT, mindupid=$MIN_DUPIDENT, softclip=$ALLOW_SOFTCLIP, BIN=$BN, part=$icpu/$ncpu \n";
  $topline =~ s/BIN/ALN_SUB_NMI=off, BIN/ unless($ALN_SUB_NMI);
  warn $topline  if($debug);
  if($ncpu>1 and $icpu>=$ncpu) { die "# err: icpu >= ncpu : -i $icpu, -ncpu $ncpu"; }

  ($haslen)= readChrtab($inchrtab, $inbam); # NOTE: this reads chr-len from inbam headers @SQ ID: LEN: unless inchrtab has it

  ($nidclass,$idclassh,$idclasslist)= read_idclass($crclassf,0,\%crlen); # cds,te class by id, $idclass->{id} = class
  $USE_CDSBAMCLASS=0 unless($nidclass and $hasCdsBam); #UPD21DEC

  if(($ncpu>1 and $icpu<$ncpu)) {   
    $outpart="$outtab.pt$icpu";
    $outchrpart= "$outchrpart.pt$icpu";
    if($outgenepart) { $outgenepart= "$outgenepart.pt$icpu"; } # outgenepart may be undef
    #obs: $rdidtab .= ".pt$icpu" if($SAVE_RDIDS and $rdidtab); # rdidtabpart ?
    warn "# output part $icpu/$ncpu nchr=$NOK to $outpart\n" if($debug);
  } 

  ## inbam must be read-ordered, ie all multimaps together, only 1 of paired reads counted now
  my $STMINFILT=0;
  my $stopt = ($SKIPR==0)? "" : ($SKIPR==1) ? " -F 0x40" : " -F 0x80"; # drop SKIPR option?
  if($MINALN > 0) { $stopt .= " -m $MINALN"; $STMINFILT=1; } #upd7
  if($samcpu>1){ $stopt.=" --threads $samcpu"; }
  
  warn "# samtools view $stopt $inbam\n" if($debug);
  if($topcount != 0) {
    my $pheadtail= ($topcount < 0) ? "tail -n$topcount" : "head -n$topcount";
    open(IN,"samtools view $stopt $inbam | $pheadtail |") or die "ERR: samtools view $stopt $inbam";
  } else {
    open(IN,"samtools view $stopt $inbam |") or die "ERR: samtools view $stopt $inbam";
  }

  if($nidclass and not $GIDPREFIX) { getGidPrefix(); } # GIDPREFIX from %$idclassh ids

  if($hasCdsBam) { 
    my $ok= openCdsBam($cdsbam, $stopt); 
    unless($ok){ $USE_CDSBAMCLASS= $hasCdsBam= 0; } 
    }
  
  my ($ntmap)= readFilter(*IN,$STMINFILT);  #? add *CDSBAM
  
  close(IN); if($hasCdsBam) { close(CDSBAM); }
  
  putcovtab( $outpart);
  putchrtab( $outchrpart); 
  putGenetab8j($outgenepart) if($SAVE_GENECOV); # GENECOVBINS==5, simplified, merge-able UPD21AUG

# MAIN_END  -------------------------

sub _max{ return($_[0] < $_[1])?$_[1]:$_[0]; }
sub _min{ return($_[0] > $_[1])?$_[1]:$_[0]; }


sub addCigarDepth_CR { # UPD22AUG.test  UPD23JUL.test2
  my( $addToBins, $aligndepth, $cr,$cb,$cigar,$rdlen,$nmap,$CutSoftCToGene,$crlen,$sflag)=@_;
  our(%crmax,%cov,%covt,%covu,%allcov,%allcovt,%allcovu); # ,@savemap,@saveid
  my($alen,$lenc,$cend,$nlen,$indel,$softclip, $cbeg, $clipb, $clipe)= (0) x 19;
  # cend == ci, alen == cm, bi == w # $cm+=$w; 
  
  my @thisdepth= (0) x $rdlen; # M bases align string
  my @thisplace= (0) x $rdlen; # S,I bases placement in align string
  # if(UPD22AUG) { # below now
  #   ($cbeg) = ($cigar =~ m/^(\d+)[SH]/)?$1:0;
  #   ($clipb)= ($cigar =~ m/^(\d+)S/)?$1:0;
  #   ($clipe)= ($cigar =~ m/(\d+)S$/)?$1:0;
  # }
  
  # FIXME dup global counts from two calls: $addToBins
  # prime align counts: 
  my $isprimary= ($sflag < SAMFLAG_2ndary)?1:0;
  my $prime2bins= ($isprimary and $addToBins);
  my $ic=0;
  my $alnend=0; #UPD23JUL
  while($cigar =~ m/(\d+)(\D)/g) #UPD22MAY: change from ([A-Z]) to m/(\d+)(\D)/, ie read all but ignore unknowns
  { 
    my($bi,$bt)=($1,$2); $ic++;
     
    if($bt eq 'M' or $bt eq 'X' or $bt eq '=') {   
      my $biend= $cend+$bi;
      for(my $i=$cend;$i<$biend;$i++){ $thisdepth[$i]=1; }  
      $alen+= $bi; $lenc += $bi;  $cend += $bi;  $alnend= $biend - 1;
      $n_align += $bi if($addToBins); # global add 22aug
      $n1align += $bi if($prime2bins); # global add 22aug
      
    } elsif($bt eq 'I') { # I means read has base missing in refseq
      #o: if($ADD_I_TO_ALEN) { $thisplace[$cend] += $bi;  }
      #t1: 
      if($ADD_I_TO_ALEN) { my $iat= $alnend;  $thisplace[$iat] += $bi;  }
      #t2: try calling these aligns == thisdepth, not place .. no help, use t1
      #t2: if($ADD_I_TO_ALEN) { my $iat= $alnend;  $thisdepth[$iat] += $bi;  }
      $n_insert += $bi if($addToBins); #UPD7f change for base count eq n_mismatch += nmi;
      $n1insert += $bi if($prime2bins); #UPD7f change for base count eq n_mismatch += nmi;
      $lenc += $bi;  $indel++;
      
    } elsif($bt eq 'D') {  #  D means refseq has base missing in read
      $n_delete += $bi if($addToBins); #UPD7f change for base count;
      $n1delete += $bi if($prime2bins); # 
      $cend += $bi; $indel++;
      
    } elsif($bt eq 'H') { #UPD22JUL: dont count H hard clip for minimap, others, for long-reads w/ suppl align
      $cbeg=$bi if($ic==1); # $n_hardclip += $bi  if($addToBins);
      unless(NOHARDCLIP){ $lenc += $bi; $thisplace[$cend] += $bi;}  

    } elsif($bt eq 'S') {
      $softclip += $bi; 
      $n_softclip += $bi if($addToBins);
      $n1softclip += $bi if($prime2bins);
      if($ic==1) { $cbeg= $clipb= $bi; }
      elsif($ic>1) { $clipe= $bi; } #?  and $cigar=~m/$bi$bt$/
      
      #t3: limit softclip thisplace to primary align only? prime2bins : no obv effect?
      if($ALLOW_SOFTCLIP and $prime2bins) { # and $TEST_UNBASES
      my $bic= $bi;    
      if($addToBins) { 
        # check aligndepth for Suppl aln remove from Sclip
        if($bi eq $clipe and $ic > 1) {
          my $alnb=0;  
          for(my $i=$rdlen-$clipe; $i<$rdlen; $i++) { $alnb++ if($aligndepth->[$i] > 0); }
          # for(my $i=$lenc; $i<=$rdlen; $i++) #? should be? $i=$rdlen-$clipe; $i<$rdlen;
          $bic= _max(0, $bic - $alnb);
        } elsif($ic == 1) { # ($bi eq $clipb)
          my $alnb=0;
          for(my $i=0; $i<$clipb; $i++) { $alnb++ if($aligndepth->[$i] > 0); }
          $bic= _max(0, $bic - $alnb);
        }
      }
      
      ## thisplace FIXME truncate bi to ends of this-cr, maybe, for genes only? need option test
      if($CutSoftCToGene){  
        my $btoend= 1 + _min( $cend - 0, $crlen - $cend);
        $bic= _min($bic, $btoend); $bic=0 if($bic<0);
      }
      my $iat= $alnend; if($iat < $clipb){ $iat=$clipb; }  #UPD23JUL 
      $thisplace[$iat] += $bic; 
      #o: $thisplace[$cend] += $bic; 
      }
      
      $lenc += $bi;  #? change to bic? no: lenc used for full readlen, needs to match softclip += nnn,

    } elsif($bt eq 'N') { 
      $n_intron++  if($addToBins);  $cend += $bi; # cend not changed for HISP; 
    } elsif($bt eq 'P') {  # what is P? Pad, but used how?
      $cend += $bi;
    } else {
      # unknown here, what? record?
    }
  }

  unless($addToBins) {
    return($alen,$lenc,$cend,$softclip, \@thisdepth, $cbeg); #??  \@thisplace not needed
    
  } else { # $addToBins
    #UPD22AUG ? need to add unplaced bases for SAMFLAG_munmap, unmapped read of pair, double? bases to covp
    my $placeUnmappedMate= ($sflag & SAMFLAG_munmap)?1:0; # UPD22AUG
    my($bi0,$ib,@biv,@covs)=(-1,-1);
    my($tcovm,$tcovp,$tcovt,$tcovu)= (0) x 4; #UPD22AUG new returns
    my $bdepth= 1.0/$BN; 
    for(my $i=0; $i<=$cend; $i++) {
      my $tdepth= $thisdepth[$i];
      my $tplace= $thisplace[$i]; 
      next unless($tdepth or $tplace);
      my $adepth= $aligndepth->[$cbeg+$i]||0; # 1..nmap
      ## .. above clipb,e trim replaces this?
      #if($adepth > 0 and $tdepth < 1) { # and tplace>0 * LIKELY bad test, need other way to cut Supp Aln from Sclip ends
      #  next; # this is Sclip of 1align, sub-align has it, dont count in 1st align
      #}
      my $bi= int( ($cb+$i)/$BN );
      if($bi > $bi0) { push @biv, $bi ; $bi0= $bi; $ib++; }
      
      # maybe: tplace=0 if($adepth and not $tdepth); # for S clip ends of 1aln, adepth of sub-align
      # $covs[$ib]{'covp'} += ($tdepth+$tplace)/$BN; # align+place depth .. /adepth  also?
      my $covp= ($tdepth+$tplace)/$BN; 
      #UPD23JUL: ?? if($tplace > $tdepth and $adepth<1) { } # check for misplace ??
      $covp = $covp/$adepth if($adepth>1);
      if($placeUnmappedMate){ $covp= 2*$covp; } # best guess?
      $covs[$ib]{'covp'} += $covp; $tcovp += $covp; # align+place depth .. /adepth  also?
                
      my ($rvd,$uvd) = (0,0); # rdv = total rmap / dup maps
      if($adepth > 1) { 
        $rvd= $bdepth/$adepth; # was (1/adepth)/BN
      } elsif( $tdepth>0 ) { 
        $uvd= $rvd= $bdepth;
      } else {
        next;
      }
      $covs[$ib]{'covt'} += $bdepth;  $tcovt += $bdepth;
      $covs[$ib]{'covm'} += $rvd;  $tcovm += $rvd;
      $covs[$ib]{'covu'} += $uvd;  $tcovu += $uvd; # uniq read depth; tcovu ??
    }
    return($alen,$lenc,$cend, \@biv, \@covs, $cbeg, $tcovm,$tcovp,$tcovt,$tcovu); # diff ret()
  } # addToBins
}

=item thisplace/covp test1sum

** NOTE: much lower Cmed for KUucg, CDSbus than beet22c2test .. is this right calc?
..  >> GSE and CU=~17 above unplace-uncorrected (16), below c2test corrected covtab (20)
.. note nounbases=0 here, possible double add I,S ?
.. note Read bases counts are same as above, counted separately
.. note covspan much lower??, 

beet18usda_chr_SRR12705892lm_mim.cc9n_sum.txt
Source=Beet18Usda, KUlow=16.46, KUhigh=16.68, FlowcytSize=705-900 Mb Formula_LN/C=749.9-759.9 Mb -Contam (beet18usda_chr)
_____   ______  Low     Low     High    High    Total   MapTotal         
Item_   Obs.Mb  Est.Mb  xCopy   Est.Mb  xCopy   tCopy   Est.Mb  Description
---------------------------------------------------------------------------
allasm  501     747     1.72    757     1.74    2.00    871     measured assembly
_total  539     785     .       795     .       .       909     total assembly  - contam
uniqasm 348     473     1.35    479     1.37    1.07    476     asm with unique gDNA
dupasm  153     267     2.54    270     2.58    4.10    395     asm with multimap gDNA
cdsasm  456     731     1.81    741     1.84    2.14    839     asm with CDS-mapped gDNA
CDSann  56      77      1.31    78      1.33    1.48    75      asm with CDS annotations
TEann   42      58      1.48    59      1.50    2.04    62      asm with Transposons
RPTann  29      41      1.82    42      1.84    1.49    54      asm with simple Repeats
NOann   382     583     1.79    591     1.81    2.11    693     asm without annotations
CONTAM  0.5     75.0    144.67  76.0    146.60  157.14  76.0    asm contamination
---------------------------------------------------------------------------
  Total span=541 Mb, covspan=454.1, gaps=0 Mb for assembly beet18usda_chr
  Genome Size Est= 810.9-821.8 Mb -Contam (Nread), 749.9-759.9 Mb -Contam (Maprd), for readset SRR12705892lm,
   for Size=LN/C, Cov=16.46,16.68, N_reads=1397832, N_maprd=1301490,93.1%, L_readlen=10572
   using LN=14777.73 read bases, not L*N=14777.88 for uneven L readlen
   Read bases: aligned=637.3 M,10560581537, unplaced=235.8 M,3906998840, unmapped=18.7 M,310146860, contam=75.5 M,1251197700, total=891.8 M,14777727237

A Best C Depth Est. from part: CMgenes
Part    Cmed    Cave    Cn      Cerr    Crank
CMgenes 16.46   26.41   11240   9.9500  3
KUucg   16.68   17.95   1167    1.2700  2
CDSbus  17      19.67   29090   2.6700  2
uniqasm 17      22.55   3477388 5.5500  1
CDSann  18      21.84   564284  3.8400  0
allasm  18      28.63   5008951 10.6300 0
NOann   18      29.86   3820681 11.8600 0

versus beet18usda_chr_SRR12705892lm_mim.cc9m_sum.txt : no CU.unplace correction
.. nearly same CU part estimates ?? same covspan ??
  Total span=541 Mb, covspan=454.1, gaps=0 Mb for assembly beet18usda_chr

beet.pblo.mim cov/tspan .839; ba .691 bp .285 bu .022; 
 GSEt 854 GSEm 835  GSEa 591 of cu=16,  no-unplace cov, GSEa is well below GSEm,t for uncov.cor
 GSEt 684 GSEm 668  GSEa 473 of cu=20
Part    Cmed    Cave    Cn      Cerr    Crank
CMgenes 16.46   26.41   11240   9.9500  3
CDSbus  16      17.05   29090   1.0500  2
KUucg   15.54   16.84   1167    1.3000  2
uniqasm 16      17.59   3477388 1.5900  1
CDSann  17      18.05   564278  1.0500  0

versus beet22c2test_SRR12705892lm_mim.sum.txt  : aCovM has unplace added from 1st map, CU.unplace corrected
Source=Beet18Usda, KUlow=16.46, KUhigh=20, FlowcytSize=705-900 Mb Formula_LN/C=625.6-760.1 Mb -Contam (beet18usda_chr)
Item_	Obs.Mb	Est.Mb	xCopy	Est.Mb	xCopy	tCopy	Est.Mb	Description
---------------------------------------------------------------------------
allasm	533	666	1.30	810	1.58	2.09	844	measured assembly
uniqasm	329	352	1.08	427	1.31	1.18	431	asm with unique gDNA
dupasm	205	316	1.66	384	2.02	3.56	413	asm with multimap gDNA
---------------------------------------------------------------------------
  Total span=541 Mb, covspan=511, gaps=0 Mb for assembly beet18usda_chr
  Genome Size Est= 676.5-822.0 Mb -Contam (Nread), 625.6-760.1 Mb -Contam (Maprd), for readset SRR12705892lm,
   for Size=LN/C, Cov=16.46,20, N_reads=1397832, N_maprd=1301490,93.1%, L_readlen=10572
   using LN=14777.73 read bases, not L*N=14777.88 for uneven L readlen
   Read bases: aligned=579.3 M,10560581537, unplaced=214.3 M,3906998840, unmapped=17.0 M,310146860, contam=68.5 M,1247905700, total=810.6 M,14777727237

beet22.pblo.c2test covspan .944 ba .688 bp .288 bu .022
  GSEt 676  GSEm 661 cu=20

A Best C Depth Est. from part: CMgenes
Part    Cmed    Cave    Cn      Cerr    Crank
CMgenes 16.46   26.41   11240   9.9500  3
KUucg   20      21.18   1116    1.1800  2
CDSbus  20      21.72   29216   1.7200  2
uniqasm 20      21.58   3286573 1.5800  1
CDSann  21      23.52   577495  2.5200  0


=item thisplace/covp test1

  .. problem w/ gene.cds ends collecting large Sclip bases
  .. add some end-of-chr/gene S softclip block?
      if(bin($cb+$i) == 0 or lastbin) thisplace - softclip ?? for genes only?
  .. problem also at gene intron points, not known..
  .. separate I from S bases? Sclip only at ends?  truncate S bases to cds span?

  .. BUT for chr.covtab, this end-of-chr softclip bunch may be desired
cat beet18usda_chr_SRR12705892lm_mim.cc9n.covtab | grep -v PCN | head
   aCovM => aCovP (M+I+S), CovU = M+I+S 1st align, CovM = M 1st align
   Note CovU doesnt have hugh softclip bp that aCovM does, why?
#ChrID          Pos     CovT    CovM    CovU    aCovT   aCovM   aCovU
beet18u_Chr1    100     1.96    19.93   20.75   20.66   618.11  19
beet18u_Chr1    200     1.97    22.6    23.49   25.37   24.68   21.78
beet18u_Chr1    300     1.9     25.27   26.58   27.97   28.31   24.44
beet18u_Chr1    400     1.92    26.34   27.45   29.03   29.31   25.5
beet18u_Chr1    500     1.88    26.46   28      29.68   28.82   25.63
beet18u_Chr1    600     1.94    26.91   28      30.95   30.06   26.1
  
beet15ref1cds_SRR12705892lm_mim.cc9n_sum.txt
  GSEt is okay
  Total span=31.5 Mb, covspan=20.7, gaps=0 Mb for assembly beet15ref1cds
  Genome Size Est= 809.3-897.8 Mb (Nread), 359.7-399.0 Mb (Maprd), for readset SRR12705892lm,
   for Size=LN/C, Cov=16.46,18.26, N_reads=1397832, N_maprd=621281,44.4%, L_readlen=10572
   Read bases: aligned=35.0 M,608173477, unplaced=459.4 M,7974453802, unmapped=356.9 M,6195099958, total=851.3 M,1477772
   
 .. high Cave here due to this problem, but Cmed maybe ok
A Best C Depth Est. from part: CMgenes
Part    Cmed    Cave    Cn      Cerr    Crank
CMgenes 16.46   26.41   11240   9.9500  3
KUucg   18.26   61.89   926     43.6300 2
CDSbus  24      172.63  19195   148.6300        2
uniqasm 21      253.63  165795  232.6300        1
CDSann  28      299.28  300474  271.2800        0

.. newer beet22c2test_SRR12705892lm_mim.sum.txt
  Genome Size Est= 676.5-822.0 Mb -Contam (Nread), 625.6-760.1 Mb -Contam (Maprd), for readset SRR12705892lm,
A Best C Depth Est. from part: CMgenes
Part    Cmed    Cave    Cn      Cerr    Crank
CMgenes 16.46   26.41   11240   9.9500  3
KUucg   20      21.18   1116    1.1800  2
CDSbus  20      21.72   29216   1.7200  2
uniqasm 20      21.58   3286573 1.5800  1
CDSann  21      23.52   577495  2.5200  0

.. old test9bf/beet18usda_chr_SRR12705892lm_mim.cc9m_sum.txt
  Genome Size Est= 830.5-854.4 Mb -Contam (Nread), 768.7-790.8 Mb -Contam (Maprd), for readset SRR12705892lm,
CMgenes 16.46   26.41   11240   9.9500  3
CDSbus  16      17.05   29090   1.0500  2
KUucg   15.54   16.84   1167    1.3000  2
uniqasm 16      17.59   3477388 1.5900  1
CDSann  17      18.05   564278  1.0500  0

.. old test9bf/beet15ref1cds_SRR12705892lm_mim.cc9m_sum.txt
  Genome Size Est= 897.8-1268.5 Mb (Nread), 399.0-563.8 Mb (Maprd), for readset SRR12705892lm,
Part    Cmed    Cave    Cn      Cerr    Crank
CMgenes 16.46   26.41   11240   9.9500  3
KUucg   11.65   13.96   926     2.3100  2
CDSbus  13      10.38   19179   2.6200  2
uniqasm 14      17.89   165795  3.8900  1
CDSann  15      22.76   300190  7.7600  0
      
 grep -A2 't1.100    ' beet15ref1cds_SRR12705892lm_mim.cc9n.covtab.pt2 | head -22
beet:g104900682t1	100	0	0	0	2.81	189.56	2.81
beet:g104900682t1	200	0	0	0	2.94	3.04	2.94
beet:g104900682t1	300	0	0	0	2.93	3.05	2.93

beet:g104895199t1	100	0	0	0	0.4	57.18	0.4
beet:g104895199t1	200	0	0	0	0.95	0.96	0.95
beet:g104895199t1	300	0	0	0	0.92	0.96	0.92

beet:g104882835t1       100     0       0       0       6.45    502.11  6.45  <<
beet:g104882835t1       200     0       0       0       6.81    7.3     6.81

beet:g104882835t1       500     0       0       0       6.78    7.1     6.78
beet:g104882835t1       600     0       0       0       2.67    630.59  2.67  <<
beet:g104882835t1       700     0       0       0       1.96    5.1     1.96
beet:g104882835t1       800     0       0       0       7.6     519.33  7.6   <<
beet:g104882835t1       900     0       0       0       11.15   267.79  11.15 <<
beet:g104882835t1       1000    0       0       0       14.55   15.57   14.55
beet:g104882835t1       1100    0       0       0       15.51   19.46   15.51
beet:g104882835t1       1200    0       0       0       15.59   25.75   15.59
..
beet:g104882835t1       3300    0       0       0       21.3    31.91   21.3
beet:g104882835t1       3400    0       0       0       21.22   22.97   21.22
beet:g104882835t1       3500    0       0       0       18.01   325.59  18.01  <<
beet:g104882835t1       3600    0       0       0       4.28    864.18  4.28   <<
beet:g104882835t1       3700    0       0       0       3.91    4.12    3.91
beet:g104882835t1       3800    0       0       0       3.85    4.05    3.85
beet:g104882835t1       3900    0       0       0       3.76    3.98    3.76
beet:g104882835t1       4000    0       0       0       1.99    53.56   1.99  <<
  
=cut


sub putShortmap { 
  my($rdid, $rdlen, $nokid, $saveset, $cdsread, $geneids, $genebins)=@_;
  our(%crmax,%cov,%covt,%covu,%allcov,%allcovt,%allcovu,%allcovp); # ,@savemap,@saveid

  my @geneids=(); if($geneids) { if(CDSRETREF) { @geneids= @$geneids; } else {  @geneids= split",",$geneids; } }
  my @genebins=(); if($genebins) { if(CDSRETREF) { @genebins= @$genebins; } else {  @genebins= split",",$genebins;  } }
  
  my(@thismap,@aligndepth,%crids); @aligndepth=();
  my($nmap,$ndupmap,$topaln,$mindupaln,$lcr)=(0) x 9; # ndi   
  my $nmapin= @$saveset;
  use constant { ADD1ST => 0, ADD2BINS => 2 };
  use constant GENEBIN => 2000; # DEBUG_GENELOC: should be option, boost default to 5k ?
  my $GENEBIN2BN=  GENEBIN / $BN;
  my $CUT_SOFTCLIP= ($ALLOW_SOFTCLIP and not $TEST_UNBASES);
  # UPD22AUG: reuse option -[no]softclip w/ TEST_UNBASES, now unused to count/notcount Sclip in aCovP
  
  my $CutSoftCToGene= ($hasCdsBam)? 0 : 1; #UPD22AUG, need proper flag this data is from cds.bam
  
  for my $saver (@$saveset) {
    my($cr,$cb,$cigar,$nmi,$sfl)= @$saver;
    
    my $isprimary= ($sfl < SAMFLAG_2ndary)?1:0;
    my $crlen=0; if($CutSoftCToGene){ $crlen= $crlen{$cr} || $BN* $crmax{$cr} || 0; }
    
    my($alen,$lenc,$cend,$softclip, $thisdepth, $cbeg)= 
      addCigarDepth_CR(ADD1ST, [], $cr,$cb,$cigar,$rdlen,$nmapin,$CutSoftCToGene,$crlen,$sfl);
      
    if($ALN_SUB_NMI and $nmi>0){ $alen -= $nmi; } # dun: $n_mismatch += $nmi; 
    if($CUT_SOFTCLIP and $softclip>0) { $lenc -= $softclip;  } # move: $n_softclip++; cigar bases like n_insert
    
    # UPD22AUG: check, change or drop? this minalign filter?
    my $mapbad=0;    
    if($MINALN>0) { 
      $mapbad=1 if($alen < $MINALN); # note same test used above in readFilter()
    } else { # sample lenc
      my $mina=0;
      if(UPD22JUL and not $CUT_SOFTCLIP) {
        $mina= ($lenc - $softclip ) * $MIN_IDENT; 
      } else {
        $mina= $lenc * $MIN_IDENT; 
      }
      $mina= MIN_MINALN if($mina < MIN_MINALN);
      $mapbad=1 if($alen < $mina);
      
      if($topaln == 0) { # sample only 1st map
      $nsample++; push @sreadlen, $lenc; 
      if($nsample >= NSAMPLE) {
        @sreadlen = sort{$b <=> $a} @sreadlen; $readlen= $sreadlen[ int($nsample/2) ];
        my $areadlen= ($n_readid<2) ? $readlen : int(0.5 + $sum_readlen/$n_readid); # UPD7f
        # if($areadlen != $readlen){ } #some warning, use which?
        $MINALN= int( $readlen * $MIN_IDENT); # was $MINALN= $sminaln / $nsample; 
        $MINALN= MIN_MINALN if($MINALN < MIN_MINALN); # set higher? 10? 30?
        $MINALN= MAX_MINALNCDS if(UPD22JUL and $MINALN > MAX_MINALNCDS); #** longread adjust
        
        $MINALNCDS= $MINALN; # for now, for getCdsRead(), maybe set below chr minaln due to intron/ends misses, maxmincds?
        warn "# readlen=$readlen, avelen=$areadlen, minaln=$MINALN from $nsample\n" if($debug);
        }
      }
    }

    if($isprimary) { # UPD22JUL and 
      $sum_readlen += $rdlen;  # UPD22JUL

      # UPD22AUGfinal? drop this TEST_UNBASES for new aCovP col
      # UPD22AUGfinal? keep sum_rdalen, sum_rdalign? esp. for SAMFLAG_munmap
      if(not UPD22AUGfinal and $TEST_UNBASES and not $mapbad) { # == 0; count unbases 1st read here only? 
        # my $unbases= $lenc - $alen; # and block  $lenc -= $softclip above
        $crdtype{$cr}{'bases'} += $lenc;  $sum_rdalen  += $lenc; # total unbases = sum_rdalen - sum_rdalign   
        $crdtype{$cr}{'align'} += $alen;  $sum_rdalign += $alen; # new totals, output in #pt comments 
       
        my $bdepth= 1.0/$BN; # bin depth, may want to change all to int 1, ie base count in bin
        my $adepth= $bdepth; # dont 2x for aligndepth
        # * Opt to count in TEST_UNBASES, unmapped read of mapped pair
        if($sfl & SAMFLAG_munmap) { # use this lenc? or find length of mate?
          $crdtype{$cr}{'bases'} += $lenc;  $sum_rdalen  += $lenc;
          $bdepth= 2.0/$BN;  #? if(SAMFLAG_munmap) count lenc in bins twice? not too clean, but maybe wanted
        }
        
        #cend vs lenc .. not same but can use max to iterate
        #UPD22AUG: this may be wrong: lenc has M+I+S bases, cend has M+D bases; dont count D for depth
        # also pre-S should iterate below cb, eg: cig=1111S 222M ..  i= -1111..0..222 
        # my $lmax= _max($lenc,$cend); # cend shorter or same
        my $lmax = $lenc;
        
        for(my $i=0; $i<$lmax; $i++) { 
          my $bi= int( ($cb+$i)/$BN );
          $covu{$cr}[$bi] += $bdepth; # 3rd col, ALL read bases, aligned and un-aligned, unbases = all/col3 - aln1/col2 
          if($thisdepth->[$i]) { $cov{$cr}[$bi]  += $adepth; } # 2nd col/te align1st
        }   
      }  
    }

    if($mapbad) { } # added, maybe 2nd map better?
    elsif($topaln == 0) { # maybe/dont change here to isprimary == ($sfl & 0x900 == 0) ;  some 2ndary are 1st in @$saveset
      $topaln=$alen; 
      $mindupaln= $topaln * $MIN_DUPIDENT;
      $crdtype{$cr}{'mread'}++; 
    } elsif($sfl & SAMFLAG_2ndary) {
      $mapbad=2 if( $mapbad or $alen < $mindupaln); # == $topaln * $MIN_DUPIDENT # count? n_dupbad   
    } elsif($sfl & SAMFLAG_suppl) {
    
    } else {
      # note: outoforder topaln == 0 may be SAMFLAG_suppl
      $mindupaln= $alen * $MIN_DUPIDENT if($alen > $topaln);
    }
    
    if($mapbad){ 
      if($mapbad==2){ $n_dupbad++; } else { $n_mapbad++; }  
    } else { 
      $nmap++; 
      $ndupmap++ if($sfl & SAMFLAG_2ndary);
      $n_mapok++; # global: num maps/read id, count after mapbad, before crok

      # here, need to skip not crok, but count in nmap
      for(my $i=0; $i<$cend; $i++) { $aligndepth[$cbeg + $i] += $thisdepth->[$i]; }# UPD22JUL cbeg + i
      push @thismap, $saver; # NEW for ADD2BINS
      $crids{$cr}++; #outgenetab
    }

    $lcr= $cr;      
  } # @saveid
  
  $lcr=""; my(%gbins,%crtlist);  my $imap=0;

  my($cds1,$cds2,$cds3)=(0) x 3;
  if($cdsread > 0) {
     $cds1= ($cdsread & 1);
     unless(UPD22AUG) {
     $cds2= ($cdsread & 2);
     $cds3= ($cdsread & 4);
     }
  }

  for my $saver (@thismap) {
    my($cr,$cb,$cigar,$nmi,$sfl)= @$saver;

    my $crlen=0; if($CutSoftCToGene){ $crlen= $crlen{$cr} || $BN* $crmax{$cr} || 0; }
    my($alen,$lenc,$cend,$biva,$covs, $cbegx, $tcovm,$tcovp,$tcovt,$tcovu)= 
      addCigarDepth_CR(ADD2BINS, \@aligndepth, $cr,$cb,$cigar,$rdlen,$nmap,$CutSoftCToGene,$crlen,$sfl); # 
    ++$imap;
    # return($alen,$lenc,$cend, \@biv, \@covs, $cbeg, $tcovm,$tcovp,$tcovt,$tcovu) = addCigarDepth_CR(ADD2BINS..)

    #o:  my $rty= ($nmap==1) ? 'uniq' : ($nmap>1) ? 'mult' : 'zero';
    my $rty= ($ndupmap>0) ? 'mult' : ($nmap>0) ? 'uniq' : 'zero'; # longr Suppl parts count as uniq type

    $crdtype{$cr}{$rty} += $nmap;
    # crdtype{cr} add ?? $tcovm,$tcovp,$tcovt,$tcovu for chrtab
    $crdtype{$cr}{'tcovm'} += $tcovm;
    $crdtype{$cr}{'tcovp'} += $tcovp;
    my $be= int(($cb+$cend)/$BN);  
    $crmax{$cr}=$be if($be>$crmax{$cr});
    
    # UPD22AUG, PUT_covp_in_allcov add chr sum ??  nreads/nmap/.. for most of crdtype dont work for longreads, variable length
    # .. need to change crdtype cols from nreads to nbases?
    # .. also nmap == 1 => uniq is bad measure when long read is Suppl subset mapped in pieces,
    # ..  count ndupmaps from Secondary not Suppl: rty = ndupmap > 0 ? mult : nmap > 0 ? uniq : zero
    # $crdtype{$cr}{'covp'} += $covp_total;  #new, per chr, add col to chrtab allbases += $lenc

    if(UPD22AUGfinal) { 
      # UPD22AUGfinal? keep sum_rdalen, sum_rdalign? esp. for SAMFLAG_munmap
      # count bug: sum_rdalen >> sum_bases, for beet_SRR12705892lm_mim.ci9n.covtab
      # .. adding too much from suppl aligns? some are dups? No, see addCigarDepth_CR() lenc includes all Sclip,
      # .. and each Suppl align, so adding same Suppl align read bases 2x; try tcovp instead? 
      # beet_SRR12705892lm_mim.ci9n.covtab.pt9 : sum_bases 460742056, sum_align 402411012, sum_alength 529944763,  n_align 1438097818, 
      # beet_SRR12705892lm_mim.cj9n.covtab.pt9 : sum_bases 460997026, sum_align 3375744.4112804, sum_alength 4028003.59795722,  n_align 1437391872,
      # >> sum_align 337574441 73% ?low, sum_alength 402800360 87% of sum_bases
      # sum_align diff due to *mult* map correction, want? NO, since S_2ndary excluded here
      # another way: addcig return lenc - Sclip/Suppl duplicates
      # yetanothway: add to sum_rdalen only for prime align, add to sum_rdalign for prime+suppl
 
      if($sfl & SAMFLAG_2ndary) { 
        $sum_duplen += $alen;
      } else {
        if($sfl < SAMFLAG_2ndary) { my $lc=($sfl & SAMFLAG_munmap) ? 2*$lenc : $lenc;  
            $crdtype{$cr}{'bases'} += $lc; $sum_rdalen += $lc; }
        $crdtype{$cr}{'align'} += $alen; $sum_rdalign += $alen;  # prime + suppl
        if($sfl & SAMFLAG_suppl) { $sum_supplen += $alen; } # suppl alone;  sum_prime = sum_rdalign - sum_supplen
      }

      # unless($sfl & SAMFLAG_2ndary) { # bad
      #   #NOTE: tcovp, tcovm are /BN bin counts, floats, need int(0.5+$BN*val) here
      #   #NOTE2: tcovp has SAMFLAG_munmap addon
      #   $sum_rdalen  += int(0.5+$BN*$tcovp);
      #   $sum_rdalign += int(0.5+$BN*$tcovm); 
      # }
      # unless($sfl & SAMFLAG_2ndary) { # bad
      #   my $addmum=($sfl & SAMFLAG_munmap) ? $lenc : 0;
      #   $sum_rdalen  += $lenc + $addmum; #? or tcovp
      #   $sum_rdalign += $alen; 
      # }
    }
    
    my $nb=@$biva; my $acovm=0;
    for(my $i=0; $i<$nb; $i++) {
      my $bi= $biva->[$i]; # N: bi == int( ($cb+$i)/$BN ) for i=0..cend
      my($covt,$covm,$covu,$covp)= map{ $covs->[$i]{$_}||0 } qw( covt covm covu covp);  

      $allcovt{$cr}[$bi] += $covt;  
      $allcovu{$cr}[$bi] += $covu;   # uniq read depth
 
      if($PUT_covp_in_allcov) {
        # covp *should be* >= covm, check?
        $allcov{$cr}[$bi]  += _max($covp,$covm); 
        $allcovp{$cr}[$bi] += $covm; #?? swap cols for these?
      } else {
        $allcov{$cr}[$bi]  += $covm; 
        $allcovp{$cr}[$bi] += $covp; 
      }  
      
      $acovm=  $allcov{$cr}[$bi] if($i==0); # max(acovm, new)?
      
      if($cdsread > 0) {
        # these are all total depth counts, where CDS/TE/UNK exist
        $covt{$cr}[$bi] += $covt if($cds1); # 1st col
        $cov{$cr}[$bi]  += $covt if($cds2); # 2nd col
        $covu{$cr}[$bi] += $covt if($cds3); # 3rd col .. more cols? later
      } # has cdsread
    }


    if(@genebins and $imap < 10) {
      (my $ccr=$cr) =~ s/:/_/g; # cr id cant have : here, FIXME covtab contains orig cr:id, genetab not
      my $bi= int( $cb/$BN); # use this, $DEBUG_CRCB == 0 
      
      my $crbi= "$ccr:$bi";  #??
      for my $gloc (@genebins) {
        my($igene,$gbins)= split":",$gloc; # no igene dups in glocs
        my @gbins= split" ",$gbins;
        for my $gi (@gbins) {
          $agenecrloc[$igene][$gi]{$crbi} = $acovm; #?? = nmaps
        }
      }
    }
    
    $lcr=$cr;
  }

  if(@geneids and $nmap>0) { # GENECOVBINS
    for my $igene (@geneids) {
      $agenecov[$igene]{'nreads'} ++;
      $agenecov[$igene]{'nmaps'}  += $nmap;
      $agenecov[$igene]{'nuniq'}  ++ if($nmap == 1);
    }

    for my $gloc (@genebins) { # @genebins may be empty w/ @geneids
      my($igene,$gbins)= split":",$gloc; # no igene dups in glocs
      my @gbins= split" ",$gbins;
      for my $gi (@gbins) {
        #FIXME: $gi == base val, want $gi/BN ? for [] array index 0..n
        $agenebincov[$igene][$gi]{'covm'} ++; #?? = nreads
        $agenebincov[$igene][$gi]{'covt'} += $nmap; #?? = nmaps
        $agenebincov[$igene][$gi]{'covu'} ++ if($nmap == 1); #??
      }
    }
    
  } 
      
  if(1) { 
    my @clbit= (0,1,2,4);
    if($cdsread>0) { 
      $cdsrdclass_sum[4]++; # == hit any rdclass
      for my $i (1,2,3) { $cdsrdclass_sum[$i]++ if ($cdsread & $clbit[$i]); }
    } else { 
      $cdsrdclass_sum[0]++; # this rd not in any cds-rdclass
    }  
  }
  
  return($nmap,0);
}


sub readFilter {  
  my($inhand,$STMINFILT)=@_;
  my($lid, $iid, $ireadpart, $inrdlen, $inrdlenb, $nokid,$ntmap,$nmap,$no_cdsread,$crtlist)=(0) x 19;
  my @saver;
  my($cdsread,$geneids, $genebins)=(0,0,0); # UPD21AUG, move cdsread/geneids to bam rdid rows as tags: eC:i:1, eG:Z:gid1,gid2
  if($MINALNCDS < MIN_MINALN){ $MINALNCDS= MIN_MINALN; } elsif($MINALNCDS > MAX_MINALNCDS) { $MINALNCDS= MAX_MINALNCDS; } 
  my $testAlign=  0; #  : (not $STMINFILT and not $LONGR)?1:0;
  my $fixidPAIRMAP=0;  # can auto-detect PAIRMAP from srid and flags: srid fwd == srid rev
  my $iskip= 0;

  while(<$inhand>) {
    next if(/^\W/); # sam hdr?
    my @samx= split; 
    my($rdid,$fl,$cr,$cb,$cx,$cig)=@samx;
    
    if($fl & SAMFLAG_read2){
      if($fixidPAIRMAP > 0) { $rdid .= "/2"; }
      elsif($fixidPAIRMAP==0) { $fixidPAIRMAP=($rdid =~ m,/2,)?-1:1; $rdid .= "/2" if($fixidPAIRMAP > 0); }
    }
    
    if($rdid ne $lid) {
    
      if($lid and $nokid > 0) {
        ($nmap,$crtlist)=  putShortmap( $lid, $inrdlen, $nokid, \@saver, $cdsread,$geneids, $genebins); 
        $ntmap += $nmap;
      }
      
      $lid=$rdid; $nokid=0; @saver=();      
      $ireadpart= ($ncpu > 1) ? $iid % $ncpu : 0;
      $iskip= ($ireadpart != $icpu);
      $iid++; 
      
      unless($iskip) {
        ($cdsread, $geneids, $genebins)=(0,0,0);  # update only when rdid ne lid
        if($hasCdsBam > 0) { ($cdsread, $geneids, $genebins)= getCdsRead( $rdid, $iid); }
      
        $n_readid++;
        $inrdlen= $inrdlenb= length($samx[9]);
        }
    }

    next if($iskip);  # test even for ncpu=1, icpu=1  
    $inrdlen= _max($inrdlen, length($samx[9]));
    
    if( GENECOVBINS>=4 and $only_genetab ) { #? after ireadpart == icpu, was before
      unless($geneids) { $no_cdsread++; next;  } 
    }
    
    my($nmit)= (m/NM:i:(\d+)/)?$1:0;
    my $nmi=($ALN_SUB_NMI)?$nmit:0; # for align - nmi only    
    $n_mismatch += $nmit;
    
    my $badmap= 0;
    if($fl & SAMFLAG_nomap){ $n_nomap++; $badmap=1; } ## next; #? count n_readid here
    #elsif($testAlign) {} 
       
    if($badmap>0) { 
      putBadmap($badmap, $rdid, $inrdlen, [$cr,$cb,$cig,0,$fl], $cdsread,$geneids, $genebins );
    } else {
      my $saver= [$cr,$cb,$cig,$nmi,$fl]; #? add $fl, check  0x800 supple from bwa == 2nd half map, maybe 
      push @saver, $saver;
      $nokid++ ;
    }
  } close($inhand);

  if($lid and $nokid > 0) { 
    ($nmap,$crtlist)= putShortmap( $lid, $inrdlen, $nokid, \@saver, $cdsread,$geneids, $genebins);
    $ntmap+= $nmap;  
  }  
  $n_readid++ unless($iskip); # if($ireadpart == $icpu); # always count new ids, but for nomap
  return($ntmap);
}


sub cleangid{ my($g)=@_; if($GIDPREFIX and $g =~ s/$GIDPREFIX//){ $ncutGIDPRE++; } $g =~ s/:/_/g; return $g; }

sub getGidPrefix {
  if($nidclass and not $GIDPREFIX) { # ,$idclassh
    my @gids=sort keys %{$idclassh};
    my $nh=int(@gids/2); my($da,$db,$dc)= @gids[0,$nh,-1];
    my($dl)= _min(length($da), _min(length($db),length($dc)));
    while($dl > 2) {
    my $ip=0; 
    for(my $i= $dl-2; $i > 1; $i--) {
      if(substr($da,0,$i) eq substr($db,0,$i) and substr($da,0,$i) eq substr($dc,0,$i)) {
        $ip=$i; last;
      }
    }
    if($ip>0){ 
      my $gidp= substr($da,0,$ip); my @nop= grep{ not m/^$gidp/ } @gids; 
      unless(@nop){ $GIDPREFIX= $gidp; last; } 
    }
    $dl= $ip;
    }
  }
  return $GIDPREFIX;
}
  

sub openCdsBam {
  my($cdsbam, $stopt)= @_;
  return 0 unless(-s $cdsbam); # should be ok here
  my $IGENE=0; my $skipcdsid=0; # note NCDS == IGENE
  open(CDSBAM,"samtools view -H $cdsbam |") or return 0;     # read hdr geneids, len
  while(<CDSBAM>) {
    if(/^\@SQ/){
      my($gid)= m/SN:(\S+)/; my($crlen)= (m/LN:(\d+)/)?$1:0;
      if($gid and $crlen > 0){ 
        my $ogid=$gid; $gid= cleangid($gid); 
        my $cdsread=0;
        if($USE_CDSBAMCLASS){ # UPD21DEC
          my($crt)= $idclassh->{$ogid} || $idclassh->{$gid} || 0; 
          $cdsread |= 1 if($crt =~ /CDS/i);
          $cdsread |= 2 if($crt =~ /TE/i);
          $cdsread |= 4 if($crt =~ /UNK/i);
          unless($cdsread){ $skipcdsid++; next; } #??? skip silently?
        } else {
          $cdsread=1; # default unless classified
        }
        my $igene= ++$IGENE; # or use ++$NCDS here
        $geneindex{$gid}= $igene;
        $agenesize[$igene]= $crlen; # $cdslen{$cr}= $crlen; 
        $ageneclass[$igene]= $cdsread;# UPD21DEC
        my @gbins;
        for( my $i=100; $i<$crlen; $i+=100) { push @gbins, $i; } #? reuse $BN here or other? readsize?
        if($crlen < 140) { push @gbins, 70; } # short ones, want this?
        elsif($crlen < 180) { push @gbins, 140; }
        elsif($crlen < 220) { push @gbins, 170; }
        $agenebins[$igene]= join(' ',@gbins);
        $NCDS++; 
        }
    }
  } close(CDSBAM);
  warn "# CDSBAM nid=$NCDS, nskip=$skipcdsid in $cdsbam\n" if($debug);
 
  my $cdsopt= $stopt; $cdsopt =~ s/ \-m $MINALN// if($STMINFILT);  
  warn "# samtools view $cdsopt $cdsbam\n" if($debug);
  open(CDSBAM,"samtools view $cdsopt $cdsbam |") or die "ERR: samtools view $cdsopt $cdsbam";
  return $IGENE || 1;
} 

 
sub getCdsRead {  # == getCdsRead_UPD
  my( $rdid, $iid)= @_; # , $crfl,$cr,$cb from chr.bam row cols[1..4]
  my( $cdsread, $geneids, $genebins)=(0) x 9;
  my( $moredata,$cdsrow)=($hasCdsBam > 0,''); 
  
  return ( $cdsread, $geneids, $genebins) unless($moredata);
  if($lastcdsrow) { $cdsrow= $lastcdsrow; $lastcdsrow=''; $lcdsreadid=0; $cdsiid--; }
  else { $cdsrow= <CDSBAM>; unless($cdsrow){ $moredata=0; $hasCdsBam= -1;  } } 
  # my $fixidPAIRMAP=0; # use from readFilter
  
  my @thisrows=();
  while($moredata) {
    # my($drdid,$dfl,$dgid,$dgb,$dgx,$dcig,@dsamx)= split" ",$cdsrow;    
    my($drdid,$dfl)= split" ",$cdsrow;    

    if($dfl & SAMFLAG_read2) { $drdid .= "/2" unless($drdid =~ m,/2,); } # always add to _rev id dont need:  $isPAIRMAP
    
    my $firtsmap= ($dfl < 256);
    my $thisid= ($drdid eq $rdid)?1:0;
    $cdsiid++ if($firtsmap); # always 1st map here?
    
    if($thisid) {
      $moredata=0; 
      my $badmap=($dfl & 0x4)?1:0;
      push @thisrows, $cdsrow unless($badmap);
      $cdsrow='';
      while(<CDSBAM>){ # collect all dupmaps here, read to next rid even if badmap/nodups
        my($erdid,$erfl)= split" ",$_;                 
        if($erfl & SAMFLAG_read2) { $erdid .= "/2" unless($erdid =~ m,/2,); }
        if($erdid ne $drdid) { $lastcdsrow=$_;  $cdsiid++; $moredata=0; last; } 
        else { push @thisrows, $_; } # never for dup:($efl & 0x4)
      }
    } elsif( $cdsiid > $iid) { 
      $lastcdsrow= $cdsrow; $cdsrow=''; $moredata=0;   
    } else {
      $cdsrow= <CDSBAM>; unless($cdsrow){ $moredata=0; $hasCdsBam= -1;  }
    }
  }

  if(@thisrows) {
    my $firstm= 1;
    my(@gbins,@gids,%gids);
    my $minalncds= $MINALNCDS || MIN_MINALN; # $MINALN || int( $readlen * $MIN_IDENT);  
    for my $ro (@thisrows) {
      my($grdid,$gfl,$gid,$gb,$gx,$gcig)= split" ",$ro;
      my $alen=0; while($gcig =~ m/(\d+)[MX=]/g) { $alen += $1; } # FIXME 
      my $cend=$alen; while($gcig =~ m/(\d+)[DN]/g) { $cend += $1; } # add to gend bin
      if($ALN_SUB_NMI and $ro =~ m/NM:i:(\d+)/){ $alen -= $1; }
      
      last if($alen < $minalncds); # use GENEMIN_LAST way
       
      $gid= cleangid($gid); 
      if( $gids{$gid}++) { next; } # ignore dup maps to same gene.cds
      my $igene= $geneindex{$gid} or next;
      push @gids, $igene; #  $gid; # replace w/ igene?
      
      # NOTE: this way can return @gids but no/less @gbins
      # agenebins[ig] == 100,200,300 .. but for shorties w/ 70/140 start/end
      my @allbins= split " ", $agenebins[$igene]; # this is All gene bins, pick overlaps to this read
      my @overbins;
      my $ge= $gb + $cend;

      for my $i (0..$#allbins) {
        my $ab= $allbins[$i];
        if($ab >= $gb and $ab <= $ge) { push @overbins, $i; } # i-array not ab-base
      }
       
      if(@overbins) {
        my $gbins= join(" ",@overbins);
        push @gbins, "$igene:$gbins";  # eg == 1:1 2 3, 222:2 3, 321:3 
      }
      
      if($firstm) { $minalncds= $MIN_DUPIDENT * $alen; $firstm=0; } 
    }

  if($USE_CDSBAMCLASS){ # UPD21DEC
    $cdsread=0; #UPD21DEC, set  1|2|4 class bits, may have all/none
    for my $igene (@gids) { $cdsread |= $ageneclass[$igene]; } # note @gids is geneindex igene
  } else {
    $cdsread=1 if(@gids);
  } 
  
    # if(CDSRETREF) # change ret to \@gids, \@gbins ; dont need strings
    $geneids= \@gids;
    $genebins= \@gbins;
  }
  
  return($cdsread,$geneids, $genebins);
}


sub putBadmap {
  my($badmaptype, $rdid, $rdlen, $saveset, $cdsread,$geneids, $genebins)=@_;
   
  my($cr,$cb,$cig,$nmi,$fl)= @$saveset;
  # only process bad map if is CDS read and 1st align to chr: $fl < 0x100
  
  if( $fl < SAMFLAG_2ndary) {  
    $sum_readlen += $rdlen; #UPD22JUL
    $sum_unmap   += $rdlen; # UPD21AUG
    # 'badmap' == 'covz' is cds-read missing chr align
    if( $SAVE_GENECOV and $genebins) {  
      my $lgid=0;    
      my @glocs=  @$genebins; # (CDSRETREF) ? @$genebins : split(",",$genebins); #UPD21SEP now  igene:100,200,300..
      for my $gloc (@glocs) {
        # UPD21SEP
        my($igene,$gbins)= split":",$gloc; # no igene dups in glocs
        my @gbins= split" ",$gbins;
        for my $gi (@gbins) {
          $agenebincov[$igene][$gi]{'covz'} += 1; #??
        }
        $agenecov[$igene]{'badmap'} += 1; # badmap? or covz        
        $lgid=$igene;
      }
    }
  
    if($cdsread>0) { my @clbit= (0,1,2,4); 
      $cdsrdclass_miss[4]++; # == any rdclass
      for my $i (1,2,3) { $cdsrdclass_miss[$i]++ if ($cdsread & $clbit[$i]); }
    } else { 
      $cdsrdclass_miss[0]++;  # this is for this-rd not in cdsrd set, dont need
    }
  }
    
}


sub openRead{ my($fn)=@_; my $fh;
  $fn.=".gz" unless(-s $fn or $fn=~/.gz$/);
  my $ok= (! -s $fn)? 0 : ($fn=~/.gz$/) ? open($fh,"gunzip -c $fn |") : open($fh,$fn);
  warn "#openRead($fn) = $ok\n" if $debug;
  return($ok,$fh); 
}


=item add merge readid class tables

  -- input 2+ cdste_dnamap.readids tables w/ mapto class cols, 
     should be same columns, from opts="-savereadids -crclassf cds_te.idclass" bam=xxx.bam run_sam2covtab7iu.sh 
  .. output merged cdste_dnamap.readids, adds counts in columns w/ same readid
  .. reuse mergeparts(), run after merge parts, ie separate invoke
  
=cut

sub mergeparts {
  my($mergeflag,$ofile,@pt)=@_;
  
  unless(@pt) { # need @pt > 1 ?
    @pt=`ls $ofile.pt*`; chomp(@pt); 
  }
  my $npt=@pt;
  if(@pt<1){ warn "Warn: no parts to merge for $ofile\n"; return 0; } # need @pt > 1 ?
  rename($ofile, "$ofile.old") if(-s $ofile);
  
  # is genetab like readids? ie no cb bin location column?
  my $mtype= ($mergeflag and $mergeflag =~ /readids/)? 1 : 2;
  my $dolocgene=0; # only if DEBUG_GENELOC ?
  if( GENECOVBINS >= 5) {
     $mtype=2 if($mergeflag =~ /gene/); # new genetab 2 fixed cols 0,1,2 = GeneID, GeneBin
     $dolocgene=1 if($mergeflag =~ /gene/); # 5: genetab locgene needs special merge
  } elsif( GENECOVBINS == 4) {
     $mtype=3 if($mergeflag =~ /gene/); # genetab 3 fixed cols 0,1,2 = GeneID, ChrID, ChrBin
  } elsif( GENECOVBINS == 3) { # GENECOVBINS == 4 may add back fixed col3 ChrBin
     $mtype=2 if($mergeflag =~ /gene/); # genetab 2 fixed cols 0,1 = GeneID, class    
  } elsif( GENECOVBINS == 2) {
     $mtype=3 if($mergeflag =~ /gene/); # genetab 3 fixed cols 0,1,2 = GeneID, ChrID, ChrBin
  }
  
  my $mgeneids= 0; #UPD21AUG.drop: (defined $outgenetab and $SAVE_RDIDS and $mergeflag =~ /readids/)?1:0; # GENECOVBINS >= 1

  our(%cloc);
  sub get_locgene { # dolocgene
    my($id)=@_; our(%cloc); my @oloc; 
    use constant MAX_locgene => 9;
    return(0) unless(exists $cloc{$id});
    my @loc=sort{ $cloc{$id}{$b} <=> $cloc{$id}{$a} } keys %{$cloc{$id}}; 
    my $nloc=@loc; 
    for(my $i=0; $i<$nloc; $i++) { 
      my $lc=$loc[$i]; my $nr=$cloc{$id}{$lc} || 0; 
      last if($i >= MAX_locgene or $nr <= 9); # nr <= MIN_READS
      push @oloc, "$lc\t$nr\t0"; # print "$id\tlocgene\t$lc\t$nr\t0\n"; 
    } 
    return($nloc, @oloc);
  }


  # FIXME: dang ':' in chrid bug, removed from crcovat, but orig in .covtab
  # dapmag19sk_LG7 in genetab, dapmag19sk:LG7 in covtab
  our($ncrcovat,%crcovat)=(0);
  sub read_crcovat { 
    my($crcovtab)= @_; my $nr=0;
    open(CCT,$crcovtab) or return 0;
    while(<CCT>){
      next if(/^\W/); my @v=split; my($cr,$cb,@acov)=@v[0,1,5,6,7];
      $cr =~ s/:/_/g; # cr id cant have : here, FIXME covtab contains orig cr:id, genetab not
      if($crcovat{$cr}{$cb}){ $nr++; $crcovat{$cr}{$cb}= join("\t",@acov); }
    } close(CCT);
    return($nr);
  }
  
  # table merge is adding nums in columns, need to read each part, keep all rows, 1st col = id key
  # there may be diff id keys in parts, tab is hash on ids x @val cols
  my %tab=(); my($nrow,$ncol,$ntin,$nid,$nidmiss,$it)=(0) x 9;
  my(@thead,@tcomm);
  for my $pt (@pt){
    open(IN,$pt) or next; 
    ++$it; my($pti)= ($pt =~ m/\.(pt\w+)/)?$1:"i$it";
    while(<IN>){ 
      if(/^\W/) { 
        # chrtab has #totals, covtab has #info, preserve? at least col header
        if(/^#(ChrID|ReadID|GeneID)/) { push @thead,$_ unless(@thead); }
        elsif(/^#/ and /n_mapok|total_locs|total_reads|cds_reads|minident=/){ s/#/#$pti./; push @tcomm,$_; }
        next;
      }

      my($id, $cb, @vals);
      if($mtype == 3) { my $cid; ($id,$cid,$cb,@vals)= split; $id.="\t$cid"; }
      elsif($mtype == 1) { ($id,@vals)= split; $cb=1; } 
      else { ($id, $cb, @vals)=split; }
      
      if($dolocgene and $cb =~ m/locgene/){ my($cr,$cl,$nr)=@vals; $cloc{$id}{"$cr\t$cl"}+=$nr; } 
      elsif($dolocgene and $vals[0] =~ m/^cr:/){ 
        my $crcb= shift @vals; $cb .= "\t$crcb"; # TRY_GENELOC == 2
        for my $i (0..$#vals) { $tab{$id}{$cb}[$i] += $vals[$i]; }
        if(TRY_GENELOC >= 2) { # up to TRY_GENELOC == 3?
          my($cc,$cr,$cb)=split":",$crcb; $ncrcovat++; $crcovat{$cr}{$cb}++;
        }
        # UPD21SEP21: genetab < insert covtab data for crcb: 
        # a.collect crcb hash, crcovat{cr}{cb}=1 or push @genecrat{cr}{cb}, gid? gid,gb ?
        # b. read covtab: cov{cr}{cb}=vals[5,6,7] == crcov{$cr}{$cb}[3,4,5] 
        # c. write genetab inserting cov{cr}{cb} cols
        } 
      else { for my $i (0..$#vals) { $tab{$id}{$cb}[$i] += $vals[$i]; $ncol=$i if($i>$ncol);  } }
      $ntin++;
    } close(IN);
  }
  
  # GENECOVBINS == 4, -merge=genetab, want median of chr,pos vals .. calc here?
  my($ncrcovread,$ncrcovset)=(0,0);
  if(TRY_GENELOC >= 2 and $ncrcovat>0) { #? and $dolocgene
    ($ncrcovread)= read_crcovat($outtab); # FIXME outtab == global chr.covtab file
  }
  
  open(OUT,">$ofile"); 
  print OUT @thead if(@thead);
  for my $id (sort keys %tab) { 
    # sort genetab col2: readgene,sumgene at top before 100,200,.. ok?
    my $nic=0;
    for my $cb (sort{ $a <=> $b } keys %{$tab{$id}} ) {
      my @vals= @{$tab{$id}{$cb}}; 
      
      if($ncrcovread and $cb =~ m/cr:/) { # cb == genepos\tcr:crid:crpos
        my($gpos,$cc,$cr,$crb)= split/[\t:]/, $cb;
        if(my $covs= $crcovat{$cr}{$crb}) {
          unshift @vals, $covs; $ncrcovset++; #?? ok
        } else {
          # ?? skip cr: output if no crcovat val
        }
      }
      
      if($mtype == 1) { print OUT join("\t",$id,@vals)."\n"; } # no cb for mtype == 1
      else { print OUT join("\t",$id,$cb,@vals)."\n"; } # ok for mtype=3 where id == gid\tcrid
      $nrow++; $nic++; 
      }
    $nid++; $nidmiss++ if($nic == 0);

    if($dolocgene) {
      my($nl,@oloc)= get_locgene($id);
      if($nl){ for my $oloc (@oloc) { print OUT join("\t",$id,"locgene",$oloc)."\n"; } }
    }

  }
  print OUT @tcomm if(@tcomm);
  close(OUT);

  $nid="$nid,idmiss=$nidmiss" if($nidmiss>0);
  warn "#sam2covtab merge: nparts=$npt, nrows=$nrow, ncols=$ncol, ntin=$ntin, nid=$nid to $ofile\n";
  return($nrow);
}


sub readChrtab {
  my($inchrtab, $inbam)=@_;
  my($ok,$inh);
  if($inchrtab and ($ok,$inh)= openRead($inchrtab) and $ok) { #skip inchrtab and use only sam -H sizes ??
    while(<$inh>){ next if(/^\W/); 
      my($cr,$crlen,$nread)=split;  #? UPD7 add crclass here? but chrtab now is output read counts table, no class info
      $crlen{$cr}= $crlen; $haslen++ if($crlen>0);  # may be only list of chr, crlen w/o nread
    } close($inh); 
  } else {
    open(IN,"samtools view -H $inbam |") or die "ERR: samtools view -H $inbam";
    while(<IN>) {
      #@SQ  SN:chrid LN:chrlen @SQ	SN:dmag20ug4d_sc000001	LN:1885227
      if(/^\@SQ/) {
        my($cr)= m/SN:(\S+)/; my($crlen)= (m/LN:(\d+)/)?$1:0;
        if($cr and $crlen > 0){ $crlen{$cr}= $crlen; $haslen++; }
      } elsif(/^\@/) {
        ;
      } else {
        last; # -H means only header ?
      }
    } close(IN);
  }
  return($haslen);
}

sub read_idclass {
  my($crclassf,$allclasses,$crlenh)=@_; 
  my($nid,$ncl,$ok,$inh)=(0,0,0);
  my %crclass=();   my @crclass=();
  $allclasses||=0;
  
  if($crclassf and ($ok,$inh)= openRead($crclassf) and $ok) { 
    while(<$inh>){ next if(/^\W/); 
      my($cr,$crclass,@clx)=split;  # may have more columns .. keep all ie CDS,BUSCO 
      if($allclasses and @clx){ $crclass=join(" ",$crclass,@clx); }
      $crclass{$cr}= $crclass || 0;  
    } close($inh); 
    $nid= scalar(keys %crclass);
  }
  #.. insert other way from sam hdr ids and CRTPAT?
  if($nid==0 and $CRTPAT and ref($crlenh)){
    for my $cr (sort keys %$crlenh) {
      my($crt)= ($cr =~ m/($CRTPAT)/)? $1 : 'UNK'; 
      $crclass{$cr}= $crt;
    }
    $nid= scalar(keys %crclass);
  }
  
  unless($nid>0) { @crclass=('UNK'); }
  else {
    my %crcl=(); # make list of values = classes
    map{ $crcl{$crclass{$_}}++; } keys %crclass;  # or values %crclass > not uniq
    @crclass= sort keys %crcl; # sort by count?
  }
  
  $ncl= @crclass; 
  if($ncl > kMAXIDCLASS) { # problem, cancel..
    warn "#ERR too many idclasses n=$ncl from nid=$nid of $crclassf or sam ids x CRTPAT='$CRTPAT' \n";
    $nid=0; %crclass=(); @crclass=();
  }
  warn "# read nid=$nid, nclass=$ncl from $crclassf\n" if($debug);
  return($nid,\%crclass,\@crclass);
}  


sub putcovtab {
  my($outpart)= @_;
  warn "# output $outpart\n" if($debug);
  rename($outpart,"$outpart.old") if(-s $outpart);
  open(OUT,">$outpart");
  my $areadlen= ($n_readid<2) ? $readlen : int(0.5 + $sum_readlen/$n_readid); # UPD7f
  #bad: my $readleno= ($areadlen != $readlen)?"$areadlen,median:$readlen,sumbases:$sum_readlen/$n_readid" : $readlen; #UPD22MAY
  #UPD22OCT ^^ this causes parts data diff when some parts (areadlen == readlen), for others (areadlen != readlen)
  my $readleno= "$areadlen,median:$readlen,sumbases:$sum_readlen/$n_readid"; #UPD22OCT
    
  my $sst= "#$outpart n_readid $n_readid, n_nomap $n_nomap, n_notcr $n_notcr, n_mapok $n_mapok, n_mapbad $n_mapbad, n_dupbad $n_dupbad, n_mismatch $n_mismatch, n_insert $n_insert, n_delete $n_delete, n_softclip $n_softclip, n_intron $n_intron\n";
  if(UPD22MAY){
    #UPD22AUG: these now are base counts, not n reads: n_align n_insert n_delete n_softclip n_mismatch; n_align =? sum_rdalign
    # my( $sum_readlen, $sum_unmap, $sum_supplen, $sum_duplen, $sum_rdalign, $sum_rdalen)= (0) x 9; 
    # my( $n_readid, $n_nomap, $n_notcr, $n_mapbad, $n_dupbad, $n_mapok, $n_partb,
    #    $n_intron, $n_align, $n_insert, $n_delete, $n_softclip, $n_mismatch)= (0) x 19; 
    # Note: sum_duplen is additions to sum_bases for mult-align; 
    # Note: sum_align *and* n_align == sum(cigar.M) align bases, should be same now.. may change again
    # n_softclip is bad?, n_softclip - sum_supplen is too big, maybe n_insert also .. include all align types: dupl, prime, suppl
    #beet_SRR12705892lm.cl9n.covtab.pt10 n_readid 43682, n_nomap 2961, ..
    #  sum_bases 462027362, sum_align 404220938, sum_alength 451803693, sum_supplen 74113637, sum_unmap 10223669, n_align 1449876017,  
    #  n_mismatch 98542096, n_insert 105437494, n_delete 76412774, n_softclip 1163633239 << has 2x for 2 addcigar() calls, + dupl,suppl

    #   .. need to restrict these to just prime align? prime+suppl for inserts? < no, suppl spans are in softclip
    # ( $n1intron, $n1align, $n1insert, $n1delete, $n1softclip, $n1mismatch)= (0) x 9; # base counts for primary align only
    
    $sst= "#$outpart n_readid $n_readid, n_nomap $n_nomap, n_notcr $n_notcr, n_mapok $n_mapok, n_mapbad $n_mapbad, n_dupbad $n_dupbad," 
         . " sum_bases $sum_readlen, sum_align $sum_rdalign, sum_alength $sum_rdalen, sum_supplen $sum_supplen, sum_unmap $sum_unmap, n_align $n_align, " 
         . " n_mismatch $n_mismatch, n_insert $n1insert, n_delete $n1delete, n_softclip $n1softclip\n"; 
         # drop? , n_intron $n_intron
  }
  print OUT "#cdsxchr_covtab par: minident=$MIN_IDENT, mindupid=$MIN_DUPIDENT, softclip=$ALLOW_SOFTCLIP, readlen=$readleno, minaln=$MINALN, BIN=$BN, part=$icpu/$ncpu \n";
  print OUT $sst; warn $sst if($debug);

  #  if(UPD22MAY and $TEST_UNBASES) : no change here? reusing CovU col, val is 'all-read-bases (lenc = aln + unaln bases)'
  
  # FIXME: drop cds CovT CovM CovU ; only aCovT aCovM aCovU here???
  my @ocols=qw(ChrID Pos CovT CovM CovU aCovT aCovM aCovU);
  if(UPD22MAY and $TEST_UNBASES) { @ocols[3,4]= qw(CovPA CovPT); } #?? change col labels to flag data?
  if(UPD22AUG){ 
    if($PUT_covp_in_allcov) { push @ocols, "bCovM"; } # == aCovM orig; @ocols[6]="aCovP";
    else { push @ocols, "aCovP";} # placed+align bases
  }
  print OUT "#".join("\t",@ocols)."\n";

  my @cr = keys %crmax;  
  @cr= sort{ $crmax{$b}<=>$crmax{$a} } @cr; 
  my @tocols; my $tcrmax;
  for my $cr (@cr) { 
    my $crmax= $crmax{$cr}||0; # ?? int( $crlen{$cr} / $BN)
    $tcrmax += $crmax;
    for(my $i=0; $i<=$crmax; $i++) { 
          
      # vers3h: all these now are fractional? want int of all
      my @cdsv= ($NCDS) ? ( $covt{$cr}[$i], $cov{$cr}[$i], $covu{$cr}[$i]) : (0,0,0);
      my @allv= ($allcovt{$cr}[$i], $allcov{$cr}[$i], $allcovu{$cr}[$i]);
      
      #UPD22AUG: PUT_covp_in_allcov add allcovp col ? depends on testing
      #? maybe store covp in allcovp then decide here which output cols for covm, covp
      # .. add new 9th col for other covp, covm .. need change covsum to handle that
      # do both ways? unless(PUT_covp_in_allcov) ..
      push @allv, $allcovp{$cr}[$i]; # allcovp now either aCovP or aCovM, keep both       
      
      @ocols= ( @cdsv, @allv );
      for(my $j=0; $j<scalar(@ocols); $j++) { $tocols[$j] += $ocols[$j]; } # UPD22AUG ?? add total rows: per chr? over all chr?
      
      # if(UPD22JAN and TEST_FRACTIONS)  #UPD.TEST 22JAN02; one now, drop TEST_FRACTIONS 
      # rounding aCovM to int may bias high dupx spans, with largish NCPU=32, dropping cov-fractions/32 to zero
      # where merged val would be up to ~16, ie bad for cases of UCG <= 50, bad for daphmag w/ large dupx spans.
      # Test compromise 0.00 digits, should work ok in merge (sum row vals), elsewhere
      
      @ocols= map{ (defined $_) ? int( 0.5 + 100 * $_)/100 : 0 } @ocols;

      my $ib= $BN*(1+$i);  # < end of bin, or $ib= ($i==0) ? 1 : $BN * $i;  == start of bin 
      print OUT join("\t",$cr,$ib,@ocols)."\n";   
      } 
    #? print OUT @tocols per cr  
    }
  @tocols= map{ (defined $_) ? int( 0.5 + 100 * $_)/100 : 0 } @tocols;
  print OUT join("\t","#totalrows", $BN*(1+$tcrmax),@tocols)."\n";   #UPD22AUG; needs to be comment or screw up consumers
  close(OUT); 
}

sub putchrtab { 
  my($chrtab)= @_; # or $outh
  warn "# chrtab $chrtab\n" if($debug);
  rename($chrtab,"$chrtab.old") if(-s $chrtab);
  open(OT,">$chrtab"); 
  my $areadlen= ($n_readid<2) ? $readlen : int(0.5 + $sum_readlen/$n_readid); # UPD7f
  #bad# my $readleno= ($areadlen != $readlen)?"$areadlen,median:$readlen,sumbases:$sum_readlen/$n_readid" : $readlen; #UPD22MAY
  #UPD22OCT ^^ this causes parts data diff when some parts (areadlen == readlen), for others (areadlen != readlen)
  my $readleno= "$areadlen,median:$readlen,sumbases:$sum_readlen/$n_readid"; #UPD22OCT

  print OT "#cdsxchr_chrtab: nmap,nmult,nuniq = locations, nmread,nuniq,nomap = read counts, readlen=$readleno, part=$icpu/$ncpu\n";
  if(UPD22AUGfinal or (UPD22MAY and $TEST_UNBASES)) { #UPD22AUG tcovp tcovm; UPD22AUGfinal also
  print OT "#".join("\t",qw(ChrID chrlen nmap nuniq nmult nmread nomap nbases align tcovp tcovm))."\n";
  } else {
  print OT "#".join("\t",qw(ChrID chrlen nmap nuniq nmult nmread nomap))."\n";
  }
  
  my($tcw,$trt,$tru,$trd,$trm,$tbase,$talign,$ttcovp,$ttcovm)= (0) x 19;
  # FIXME: %crlen may be empty; use crdtype or crmax
  # also for icpu parts dont want all of crlen, use crmax == found cr
  my @cr= keys %crmax;  
  @cr= sort{ $crmax{$b}<=>$crmax{$a} } @cr;  
  for my $cr (@cr) {
    my $cw= $crlen{$cr} || 0; unless($cw){ $cw= $BN* $crmax{$cr}; }
    my ($ru,$rd,$rm)=map{ $crdtype{$cr}{$_}||0 } qw(uniq mult mread); 

    #UPD22AUG: base counts to replace read counts, tcovu, tcovt also ?
    # $crdtype{$cr}{'tcovm'} += $tcovm;  == calign
    # $crdtype{$cr}{'tcovp'} += $tcovp;  == cbases

    my $rt=$ru+$rd;
    $tcw+=$cw; $trt+= $rt; $tru+=$ru; $trd+=$rd; $trm+=$rm;
    if(UPD22AUGfinal or (UPD22MAY and $TEST_UNBASES)) {
      #UPD22AUG ? replace these bases,align w/ tcovp, tcovm ?
      my ($cbase, $calign)=map{ int( $crdtype{$cr}{$_}||0 ) } qw(bases align);
      my ($tcovm, $tcovp)=map{ int( 0.5 + $BN * $crdtype{$cr}{$_}||0 ) } qw(tcovm tcovp); #UPD22AUG
      $tbase+= $cbase; $talign+= $calign; # should be same as sum_rdalen, sum_rdalign, 
      $ttcovm+= $tcovm; $ttcovp+= $tcovp;
      print OT join("\t",$cr,$cw,$rt,$ru,$rd,$rm,0,$cbase,$calign,$tcovp,$tcovm)."\n"; 
    } else {
      print OT join("\t",$cr,$cw,$rt,$ru,$rd,$rm,0)."\n"; 
    }
    
  } 

  # add $n_notcr where? similar to n_nomap
  my $otloc;
  if(UPD22MAY and $TEST_UNBASES) {
  $otloc= join("\t","# total_locs",$tcw,$trt,$tru,$trd,$trm,$n_nomap,$tbase,$talign,$ttcovp,$ttcovm) . "\n";  
  } else {
  $otloc= join("\t","# total_locs",$tcw,$trt,$tru,$trd,$trm,$n_nomap) . "\n";  
  }
  print OT $otloc; warn "#$chrtab:$otloc" if($debug);  

  my $tnread= $n_readid; # UPD7: nomap now back in n_readid
  my $tmm= $trm - $tru;
  my ($pru,$pmm,$prm,$pnomap,$pnotcr)= map{ sprintf "%.2f%%", 100*$_/$tnread; } ($tru,$tmm,$trm,$n_nomap,$n_notcr);
  my $onotcr=""; #x  ($NOK)? "notcr:$n_notcr,$pnotcr" : "";
  my $otread= join("\t","# total_reads",$tnread,"mapt:$trm,$prm","uniq:$tru,$pru","mult:$tmm,$pmm", "nomap:$n_nomap,$pnomap", "readlen=$readlen")."\n";  #,$onotcr
  print OT $otread;  warn "#$chrtab:$otread" if($debug); 
 
  if($NCDS) { # UPD7 and 
    my $cdsanyhit= $cdsrdclass_sum[4]||0; 
    my $cdsallmiss= $cdsrdclass_miss[4]||0; 
    my $cdsno= $cdsrdclass_sum[0]||0; #< no cdsrd class hit ## my $cdsnoin= $cdsrdclass_in[0]; #?
    my $cds123  = join(",",@cdsrdclass_sum[1,2,3]);
    my $cds123miss= join(",",@cdsrdclass_miss[1,2,3]);
    
    my $otread= join("\t","# cds_reads",$NCDS,"hit/miss:$cdsanyhit/$cdsallmiss","cl123hit/miss:$cds123/$cds123miss", "no_cdsrd:$cdsno")."\n";   
    print OT $otread;  warn "#$chrtab:$otread" if($debug); 
  }
  
  close(OT); 
} 


sub putGenetab8j {
  my($chrtab)= @_; # == genetab
  warn "# putgenetab $chrtab\n" if($debug);
  rename($chrtab,"$chrtab.old") if(-s $chrtab);
  open(OUT,">$chrtab"); 

  print OUT "# genetab version=$VERS\n";
  print OUT "# cols for GeneID, GPos: aCovT aCovM aCovU aCovZero = read map counts/bin as per chr.covtab\n";
  print OUT "# cols for sumgene,  1: allCovT allCovM allCovU noCov\n"; # gave=allCovT/nbins
  print OUT "# cols for readgene, 1: nrdmaps, nreads, nuniq, nomap\n";
  
  my @ocols=qw(GeneID GPos aCovT aCovM aCovU aCovZ); # aCovZ == noCov as per 8e, aCovU uniq rd cov as per other covtab 
    # adds GeneID 1st? or last? for merge better 1st
  print OUT "#".join("\t",@ocols)."\n";
  
  my $areadlen= ($n_readid<2) ? $readlen : int(0.5 + $sum_readlen/$n_readid); # UPD7f
  my $addGIDPRE= ($GIDPREFIX and $ncutGIDPRE>0);

  my $ngene= scalar(@agenesize); # scalar keys %geneindex; # 
  my($tgmaplen,$tallmap,$tallmapm,$tallmapu,$tnmaps,$tnuniq,$tnreads,$tnomap,$tnodepth,$ttnb,$tmedcovt,$tmedcovm)=(0) x 19; 
  my(@gmed,@gave,@igeneid);
  my $igmax=0; 
  for my $gid (keys %geneindex){ my $ig= $geneindex{$gid}; $igeneid[$ig]= $gid; $igmax=$ig if($ig>$igmax); }
  for(my $igene=1; $igene <= $igmax; $igene++) { # igene=1 is 1st; ugh (@agenesize)
    my $glen= $agenesize[$igene];
    my $gid= $igeneid[$igene];
    next unless($gid); #? any missing ig indices?
    my $gidout= ($addGIDPRE)? $GIDPREFIX.$gid : $gid;

    my $nreads= $agenecov[$igene]{'nreads'}||0; # merge-able count
    my $nmaps = $agenecov[$igene]{'nmaps'}||0; # merge-able count
    my $nuniq = $agenecov[$igene]{'nuniq'}||0; # merge-able count
    my $nomap = $agenecov[$igene]{'badmap'}||0;   # merge-able count  #? badmap == allcovz
    $tnmaps+=$nmaps;  $tnuniq+= $nuniq; $tnreads+=$nreads; $tnomap+=$nomap; 

    ## dont need both these sum,read count .. sum is better stat .. NO, readcount best accuracy
    print OUT join("\t",$gidout, 'readgene', $nmaps, $nreads, $nuniq, $nomap)."\n";  #upd21jul04: was off, want for match to other tabs
    #o print OUT join("\t",$gidout, 'sumgene', $alldepth, $alldepthm, $alldepthu, $nodepth)."\n";  # note: alldepth > nreads by >=10% 

    my $tnb=0; my(@covt,@covm);
    my @allbins= split" ", $agenebins[$igene]; # this is All gene bins, pick overlaps to this read
    for(my $i=0; $i<@allbins; $i++) {
      my $gi= $allbins[$i];
      my @ocols= map{ $agenebincov[$igene][$i]{$_}||0 } qw( covt covm covu covz );

      # if(UPD22JAN and TEST_FRACTIONS)  #UPD.TEST 22JAN02 
      @ocols= map{ (defined $_) ? int( 0.5 + 100 * $_)/100 : 0 } @ocols; # ( $covt,$covm,$covu,$covz );

      print OUT join("\t",$gidout, $gi, @ocols)."\n";  $tnb++;
    
      if(exists $agenecrloc[$igene][$i]) {
        #  $agenecrloc[$igene][$gi]{$crbi} = $acovm; #?? = nmaps
        my @crcb= sort keys %{ $agenecrloc[$igene][$i] }; #? sort by +cv count?
        
        my ($lcr,$lcb,$lcrcb,$cbi)=(0) x 4; my %crdbcov=();
        for my $crcb (@crcb) {
          my $cv= $agenecrloc[$igene][$i]{$crcb}||0; 
          my($cr,$cb)=split":",$crcb;
          if( $lcr eq $cr and abs($cb - $lcb) <= 2) { 
            $crdbcov{$lcrcb} += $cv;
          } else {
            $lcb= $cb; $crdbcov{$crcb} += $cv;
          }
          $lcr=$cr; $lcrcb=$crcb;
        }
        @crcb= sort keys %crdbcov;
             
        for my $crcb (@crcb) {
          # my $cv= $agenecrloc[$igene][$i]{$crcb}||0; 
          my $cv= $crdbcov{$crcb};
          # if(UPD22JAN and TEST_FRACTIONS)  #UPD.TEST 22JAN02 
          $cv=int( 0.5 + 100 * $cv)/100;
          my($cr,$cb)= split/:/,$crcb; $cb= $BN*(1+$cb); 
          print OUT join("\t",$gidout, $gi, "cr:$cr:$cb", $cv)."\n";   # needs merge fix  
        }
      
      }
    }
  }
  
  my $pMiss= ($tnreads  < 1)? 0: sprintf"%.3f",100*$tnomap/$tnreads;
  print OUT join("\t",'total','readgene', $tnmaps, $tnreads, $tnuniq, $tnomap)."\n";  #   
  close(OUT); 
  warn "#genecov: ngene=$ngene, missed=$pMiss% of nrd=$tnreads in  $chrtab\n" if($debug);   #  C.ave=$Cave,
  return($ngene);
}


__END__
