// $Header$ -*-C++-*-

// Purpose: Test script for ncap2

/* Usage: 
   ncap2 -O -v -S ~/nco/data/ncap2_tst.nco ~/nco/data/in.nc ~/foo.nc
   ncks ~/foo.nc | /bin/more */

// Check methods first

// Count number of errors
nbr_err=0;
nbr_err_ttl=0;

{
  a1=three_dmn_var_dbl.avg();
  a2=three_dmn_var_dbl.avgsqr();
  a3=three_dmn_var_dbl.max();
  a4=three_dmn_var_dbl.min();
  a5=three_dmn_var_dbl.rmssdn();
  a6=three_dmn_var_dbl.total();
   
  // Join operands together
  b1=three_dmn_var_dbl.avg($0).total();
  b2=three_dmn_var_dbl.avgsqr($1).rmssdn();
  b3=three_dmn_var_dbl.max($2).min();
  b4=three_dmn_var_dbl.min($0).max();
  b5=three_dmn_var_dbl.rmssdn($1).avgsqr();
  b6=three_dmn_var_dbl.total($2).avg();

  // Check that total handles missing values correctly  
  // two missing values
  b10[time]={1,2,3,4,5,6,7,8,9,1};
  b10.set_miss(1L);
  // all missing values
  b11[time]=1s;
  b11.set_miss(1s);
 
  // various missing values
  b12=three_dmn_var_dbl;
  // set all values to 2.0
  b12=2.0;
  b12.set_miss(1.0);
  // sprinkle a few missing values about
  b12(:,:,0:1)=1.0;

  if(fabs(a1-40.609d) > 0.01){
    print("ERROR: a1:method test\n");
    nbr_err++;
  }

  if(fabs(a1-40.609d) > 0.01){
    print("ERROR: a1:method test\n");
    nbr_err++;
  }

  if(fabs(a2-2208.145d) > 0.01){
   print("ERROR: a2:method test\n");
   nbr_err++;
  }

  if(a3 != 79L){
    print("ERROR: a3:method test\n");
    nbr_err++;
  }

  if(a4 != 1L){
    print("ERROR: a4:method test\n");
    nbr_err++;
  }

  if(fabs(a5-47.336d) > 0.01){
    print("ERROR: a5:method test\n");
    nbr_err++;
  }

  if(a6 != 2802L){
    print("ERROR: a6:method test\n");
    nbr_err++;
  }

  if(fabs(b1-322.714d) > 0.01){
    print("ERROR: b1:method test\n");
    nbr_err++;
  }

  if(fabs(b2-3095.591d) > 0.01){
    print("ERROR: b2:method test\n");
    nbr_err++;
  }

  if(b3 != 4L){
    print("ERROR: b3:method test\n");
    nbr_err++;
  }

  if(b4 != 8L){
    print("ERROR: b4:method test\n");
    nbr_err++;
  }

  if(fabs(b5-4149.576d) > 0.01){
   print("ERROR: b5:method test\n");
   nbr_err++;
  }

  if(fabs(b6-155.667d) > 0.01){
  print("ERROR: b6:method test\n");
  nbr_err++;
  }

  if(b10.total() != 44L){
  print("ERROR: b10:method test\n");
  nbr_err++;
  }

  // b11 should be full of missing vales
  if(!b11.total().missing()){
  print("ERROR: b11:method test\n");
  nbr_err++;
  }

  if(fabs(b12.total() -80.0d) > 0.01){
  print("ERROR: b12:method test\n");
  nbr_err++;
  }

  print("RESULTS block a,b: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
} // End Method test

// Scott's wind speed test 
{
  bin_nbr=3;
  defdim("bin",bin_nbr); // [nbr] Bin dimension
  wnd_min[bin]={0,1,2}; // [m s-1] Minimum speed
  wnd_max[bin]={1,2,3}; // [m s-1] Maximum speed

  results[lat,lon,bin]={
  7, 1, 1,
  6, 2, 1,
  5, 2, 2,
  6, 2, 1,
  7, 1, 1,
  5, 2, 2,
  7, 2, 0,
  6, 2, 1 };

  // Regular Vars
  bin_cnt[lat,lon,bin]=0s; // [nbr] Wind speeds in bin
  bin_flg[time,lat,lon]=0s; // [flg] Wind speed within current bin
  set_miss(bin_cnt,wnd_spd@_FillValue);

  for(bin_idx=0;bin_idx<bin_nbr;bin_idx++){
    bin_flg=(wnd_spd >= wnd_min(bin_idx) && wnd_spd < wnd_max(bin_idx));
    bin_cnt(:,:,bin_idx)=bin_flg.total($time); // [nbr] Wind speeds in bin
  }

  if((bin_cnt-results).total() !=0){
    print("ERROR: c1:Scotts test - regular vars\n");
    nbr_err++;
  }
 
  // Repeat exercise with RAM vars 
  *bin_ram_cnt[lat,lon,bin]=0s; 
  *bin_ram_flg[time,lat,lon]=0s;
  set_miss(bin_ram_cnt,wnd_spd@_FillValue);

  for(bin_idx=0;bin_idx<bin_nbr;bin_idx++){
    bin_ram_flg=(wnd_spd >= wnd_min(bin_idx) && wnd_spd < wnd_max(bin_idx));
    bin_ram_cnt(:,:,bin_idx)=bin_ram_flg.total($time); 
  }

  if((bin_ram_cnt-results).total() !=0){
    print("ERROR: c2:Scotts test - ram vars\n");
    nbr_err++;
  }
  
 ram_delete(bin_ram_cnt);
 ram_delete(bin_ram_flg);

 print("RESULTS block c: Num errors="); print(nbr_err,"%d");
 nbr_err_ttl+=nbr_err;
 nbr_err=0;
} // End Scott's Test

// More RAM var Testing
{
  *d1[$time,$lat,$lon]=three_dmn_var_dbl;

   // Value list on RHS
   d1(0,0,:)={10d,20d,30d,40d};

   // Attribute on RHS
   d1@n1={2d,4d,8d,16d};
   d1(0,1,:)=d1@n1;
    
   // Scalar on RHS
   d1(:,:,3)=88.0;

   // RAM Var on RHS
   *d2[$lon]={1d,4d,9d,16d};
   d1(2,0,:)=d2;

   ram_write(d1);

   if(fabs(d1.total()-3800d) > 0.01){ 
    print("ERROR: d1:ram test\n");
    nbr_err++;   
   }
 
   //Repeat exersise with ints;
   *d3[$time,$lat,$lon]=three_dmn_var_int;

   // Value list on RHS
   d3(0,0,:)={9,11,13,15};

   // Attribute on RHS
   d3@n1={2,4,8,16};
   d3(0,1,:)=d1@n1;
    
   // Scalar on RHS
   d3(:,:,3)=100L;

   // RAM Var on RHS
   *d4[$lon]={1,3,27,81};
   d3(2,0,:)=d4;

   // Regular var On RHS
   d5[$lon]={1,2,3,4};
   d3(8,1,:)=d5;

   if(d3.total() !=3716L){ 
    print("ERROR: d3:ram test\n");
    nbr_err++;   
   }

   print("RESULTS block d: Num errors="); print(nbr_err,"%d");
   nbr_err_ttl+=nbr_err;
   nbr_err=0;
   
   ram_delete(d3);
   ram_delete(d4);
}

// Test LHS casting - Regular var
{
   // Var on RHS 
   e1[$time]=time;
   
   // Value list on RHS
   // nb type on RHS is the type of first member
   e2[$lon]={1f,2s,3L,4};
	
   // Attribute on RHS
   e3@tst={5,25,50,100};
   e3[$lon]=e3@tst;   

   // Bare number on RHS
   e4[$lon]=99d;

   // Real-life casting
   e5[time,lat,lon,lev]=P0*hyam+hybm*PS;

   if(fabs(e1.avg()-5.5d) > 0.01){
    print("ERROR: e1: LHS cast test\n");
    nbr_err++;
   }

   if(fabs(e2.avgsqr()-7.5f) > 0.01){
    print("ERROR: e2: LHS cast test\n");
    nbr_err++;
   }

   if(e3.rmssdn() != 66L){
    print("ERROR: e3: LHS cast test\n");
    nbr_err++;
   }
   
   if(fabs(e4.total()-396d)>0.01){
    print("ERROR: e4: LHS cast test\n");
    nbr_err++;
   }

   if(fabs(e5.min()-360f) > 0.001f || fabs(e5.avg()-51254.11f) > 0.01f){
    print("ERROR: e5: LHS cast test\n");
    nbr_err++;
   }

   print("RESULTS block e: Num errors="); print(nbr_err,"%d");
   nbr_err_ttl+=nbr_err;
   nbr_err=0;
}

// Check var/att/dim quoting
{
  'u---u'=10L;
  'v...v'=20L;
  'w...w'='u---u'*'v...v';
  'u---u@kill'=10L;
  'v...v@o.one'=30L;
  f1='v...v@o.one' *100;
  f2='u---u'+40;

  defdim("t..t",5);
  f3['$lon','$lat','$t..t']=1.1d;
  f4='$t..t'.size;

  if(f1 !=3000L){
    print("ERROR: f1: ID quoting\n");
    nbr_err++;
  }

  if(f2 != 50L){
    print("ERROR: f2: ID quoting\n");
    nbr_err++;
  }

  if(fabs(f3.total()-44.0) > 0.01){
    print("ERROR: f3: ID quoting\n");
    nbr_err++;
  }

  if(f4 != 5L){
    print("ERROR: f4: ID quoting\n");
    nbr_err++;
  }

  print("RESULTS block f: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
} //end ID quoting

// Check Loops 
{

  *idx=0L;
  *jdx=0L;
  *g1=0;
  *g2=0.0;

  while(idx++ <20){
   jdx=0;
   while(jdx++<10){
    if(jdx>5) continue;
    g1+=jdx;
   }
   if(idx==12) break;
  }
 
   if(g1 !=180L){
    print("ERROR: g1: LHS loop test\n");
    nbr_err++;
  }

  for(idx=0 ; idx<10 ; idx++){
   for(jdx=10.0 ; jdx<15.0; jdx++)
     g2+=(idx+jdx); 
  }

  if(fabs(g2-825d)>0.01){
    print("ERROR: g2: LHS loop test\n");
    nbr_err++;
  }

  ram_write(g2);

  ram_delete(idx);
  ram_delete(jdx);
  ram_delete(g1);

  print("RESULTS block g: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Check missing value functions and masking
{
  h1=txyz;
  // Change all values less than 20 or greater than 80 to 2;
  *hmask= h1<20 || h1>80;

  h1=hmask*2 +!hmask*h1;

  change_miss(h1,2f);

  h2=h1.total($time,$2).max();
  h3=h1.avgsqr($x,$3).min();
  
  // check get_miss method
  h4=fll_val.get_miss();

  if(fabs(h2-315f)>0.01){
    print("ERROR: h2: masking test\n");
    nbr_err++;
  }

  if(fabs(h3-420.5f)>0.01){
    print("ERROR: h3: masking test\n");
    nbr_err++;
  }

  if(fabs(h4-(-999.0f))>0.01){
    print("ERROR: h4: get_miss() test\n");
    nbr_err++;
  }
  
  // ram_delete(hmask);

  print("RESULTS block h: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Check hyperslab normalization
// NB: This is where a limit in a hyperslab collapses to a single index 
// or the slab specified is equal to all the indices in the dimension
// e.g., three_dmn_var_dbl(1,:,:), three_dmn_var_dbl(0:9,:,1), three_dmn_var_dbl(:,0,:)
{
  i1=three_dmn_var_int(0,:,:)+three_dmn_var_int(1,:,:).reverse($lat,$lon);

  i2=three_dmn_var_int(0,0,0)+three_dmn_var_int(1,1,3);

  // NB: RHS is cast correctly  
  i3[$time,$lat,$lon]=three_dmn_var_int(1,:,:);
    
  i4=i3.avg($time); //nb i4 has dims $lat,$lon   

  // Check that missing value is preserved in a hyperslab
  i5=three_dmn_var_sht(:,:,:);

  // Check size of an un-normalizable hyperslab
  i5@size=three_dmn_var_sht(0,:,0:1).size();

  // Check min and max
  i6=three_dmn_var_sht(:,1,3); 

  if(fabs(i1.avg()-17L)>0.001){
        print("ERROR: i1: hyperslab normalization test \n");
        nbr_err++;
  }

  if(i2 != 17L){
        print("ERROR: i2: hyperslab normalization test \n");
        nbr_err++;
  }
 
  if(i4.min()!=9 || i4.max()!=16){
        print("ERROR: i3: hyperslab normalization test \n");
        nbr_err++;
  } 

  if(i5.total()-three_dmn_var_sht.total() !=0){
        print("ERROR: i5: hyperslab normalization test \n");
        nbr_err++;
  } 

  if(i5@size-4 !=0){
        print("ERROR: i5a: hyperslab size test \n");
        nbr_err++;
  } 

  if(i6.min()!=8 || i6.max()!=72){
        print("ERROR: i6: hyperslab min/max test \n");
        nbr_err++;
  } 

  print("RESULTS block i: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Check "where" structure 
{
  // start simple 
  j1=time;
  where(time > 6)
    j1=10;
  elsewhere
    j1=5;  

  j1_ttl=j1.total();

  if(fabs(j1_ttl-70.0)>0.01){
    print("ERROR: j1: simple where test\n");
    nbr_err++;
  }
  
  j2=three_dmn_var_int;
  j3[$time,lat,lon]=1;
  j4=three_dmn_var_dbl;

  where(three_dmn_var_dbl >40){
    j2=j3;
    j4=10.0;
  } elsewhere{
    j3=-1.0;
    j4=j3;
  } 
 
  j2_ttl=j3.total();
  j4_ttl=j4.total();
  
  // nb j2 type integer
  if(j2_ttl+6L != 0){
    print("ERROR: j2: where test with blocks\n");
    nbr_err++;
  }
  
  if(fabs(j4_ttl-327d)> 0.01){
    print("ERROR: j4: where test with blocks\n");
    nbr_err++;
  }

  // try missing value equal to NC_FILL_INT
  j5=time.int();
  j5.set_miss(-2147483647L);
  where(j5 > 3L)
    j5=-2147483647L;

  if(fabs(j5.total().float() - 6f) > 0.01){
    print("ERROR: j5: where test with NC_FILL_INT\n");
    nbr_err++;
  }
  
  print("RESULTS block j: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Check if /else construct
{
  k1=0;
  k2=0;
  k3=0;
  k4=0;
  k5=0;
  k6=time.int();
  k7=0;

  if(one == 0){k1=10;}else{k1=5;}

  if(k1!=5){
    print("ERROR: k1: if/else test with blocks\n");
    nbr_err++;
  }

  if(0){k2=1;} else if(1) k2=10; 

  if(k2 != 10){
    print("ERROR: k2: if/else test with blocks\n");
    nbr_err++;
  }

  if(one == 0) k3=1;
  else if(one == 0) k3=2;
  else k3=3;

  if(k3 != 3){
    print("ERROR: k3: if/else test with blocks\n");
    nbr_err++;
  }

  if(one==0) 
   {k4=1; }
   else{if(one==0) k4=2;
  else k4=3;
   }

  if(k4 != 3){
    print("ERROR: k4: if/else test with blocks\n");
    nbr_err++;
  }

  // dangling else
  if(four == 0) k5=1;
  else if(one==0) k5=2;
  else k5=3;		    

  if(k5 != 3){
    print("ERROR: k5: if/else test with blocks\n");
    nbr_err++;
  }

  if(four == 4) 
    where(time > 5)
      k6=10;
    elsewhere
      k6=1;         

  if(k6.total() != 55){
    print("ERROR: k6: if/else test with blocks\n");
    nbr_err++;
  }

  if(one == 1){k7=7;}else{k7=-7;}

  if(k7 != 7){
    print("ERROR: k7: if/else test with blocks\n");
    nbr_err++;
  }

  print("RESULTS block k: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Check irregular hyperslabs on RHS
{
  m1=three_dmn_var_int(:,0,0:1).max(); //74
  m2=three_dmn_var_int(:,0,0:1).min(); //1

  m3=three_dmn_var_int(0:1,0,0:1).total(); //22

  m4=three_dmn_var_dbl;

  m4(:,0,0:1)=4*three_dmn_var_int(:,0,0:1);

  // hyperslab with an attribute on RHS
  m5=three_dmn_var_dbl;
  m5@tst1=three_dmn_var_int(:,0,0:1);
 
  m5(:,0,0:1)=m5@tst1;

  // hyperslab with a RAM var on RHS
  m6=three_dmn_var_dbl;
  *m6a=three_dmn_var_int;
  m6(:,0,0:1)=4*m6a(:,0,0:1);    

  // hyperslab a RAM var
  *m7=three_dmn_var_int;
  m8=m7(0:1,0,0:1).total(); //22 
  
  ram_delete(m7); 
  ram_delete(m6a);

  if(m1 != 74L){
        print("ERROR: m1: Irregular hyperslab test \n");
        nbr_err++;
  }

  if(m2 != 1L){
        print("ERROR: m2: Irregular hyperslab test \n");
        nbr_err++;
  }

  if(m3 != 22L){
        print("ERROR: m3: Irregular hyperslab test \n");
        nbr_err++;
  }

  if(fabs(m4.total() -4141.0d) > 0.01d){
        print("ERROR: m4: Irregular hyperslab test \n");
        nbr_err++;
  }

  if(fabs(m5.total() -2710.0d) > 0.01d){
        print("ERROR: m5: Irregular hyperslab test \n");
        nbr_err++;
  }

  if(fabs(m6.total() -4141.0d) > 0.01d){
        print("ERROR: m6: Irregular hyperslab test \n");
        nbr_err++;
  }

  if(m8 != 22L){
        print("ERROR: m8: Irregular hyperslab test RAM var\n");
        nbr_err++;
  }

  print("RESULTS block m: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Check sorting functions
// At present only two sort functions: sort(var_exp,&sort_map), dsort(var_exp,sort_map)
{
  // Check basic sort
  n1[lat,lon]={200L,100,3,8,-10,-5,0,-9L};
  n1_st=n1.sort();
  
  // Create map from first row of elements
  sort(n1(0,:),&n1_map);

  n2[lat,lon]={-1.0,0.0,2.0,3.0,10.0,20.0,30.0,40.0};

  n2=remap(n2,n1_map);

  // Create map larger variable
  n3=remap(three_dmn_var_dbl,n1_map);

  if(n1.min() != n1_st(0,0) || n1.max()!=n1_st(1,3)){
        print("ERROR: n1: Basic sort test\n");
        nbr_err++;
  }
     
  *n_tmp[lon]={3,2,0,1L};
  if(n1_map != n_tmp){
        print("ERROR: n2: Create mapping sort test\n");
        nbr_err++;
  }
  ram_delete(n_tmp);

  if(n2(0,0) != 2.0 || n2(1,3) != 10.0){
        print("ERROR: n3: Apply mapping sort test\n");
        nbr_err++;
  }

  if(n3(9,1,0) != 79.0 || n3(9,1,3) != 77.0){
        print("ERROR: n4: Apply mapping sort test\n");
        nbr_err++;
  }

  print("RESULTS block n: Num errors="); print(nbr_err,"%d");
  nbr_err_ttl+=nbr_err;
  nbr_err=0;
}

// Results summany
print("RESULTS SUMMARY: total errors=");print(nbr_err_ttl,"%d");
