//===========================================================================
// SINTEF LSMG library - version 1.1
//
// Copyright (C) 2000-2005 SINTEF ICT, Applied Mathematics, Norway.
//
// This program is free software; you can redistribute it and/or          
// modify it under the terms of the GNU General Public License            
// as published by the Free Software Foundation version 2 of the License. 
//
// This program is distributed in the hope that it will be useful,        
// but WITHOUT ANY WARRANTY; without even the implied warranty of         
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
// GNU General Public License for more details.                           
//
// You should have received a copy of the GNU General Public License      
// along with this program; if not, write to the Free Software            
// Foundation, Inc.,                                                      
// 59 Temple Place - Suite 330,                                           
// Boston, MA  02111-1307, USA.                                           
//
// Contact information: e-mail: tor.dokken@sintef.no                      
// SINTEF ICT, Department of Applied Mathematics,                         
// P.O. Box 124 Blindern,                                                 
// 0314 Oslo, Norway.                                                     
//
// Other licenses are also available for this software, notably licenses
// for:
// - Building commercial software.                                        
// - Building software whose source code you wish to keep private.        
//===========================================================================

#include <SmoothMatrix.h>

#ifdef WIN32
#include <minmax.h>
#endif

#include <math.h>
#include <algorithm>
#include <stdlib.h>

#include <iostream>
using namespace std;

double SmoothMatrix::innerProduct_[3][4] = {
  {151./315., 397./1680., 1./42., 1./5040.},
  {2./3., -1./8., -1./5., -1./120.},
  {8./3., -3./2., 0., 1./6.}
};

double SmoothMatrix::innerProductQd0_[3][3] = {
  {599./1260.,-99999999999999.,-99999999999999999.},
  {151./630.,59./280.,-999999999999999.},
  {1./252.,43./1680.,1./84.}
};
double SmoothMatrix::innerProductQd1_[3][3] = {
  {37./60.,99999999999999.,99999999999999999.},
  {1./3.,-11./60.,999999999999999.},
  {1./20.,7./120.,-1./10.}
};
double SmoothMatrix::innerProductQd2_[3][3] = {
  {7./3.,99999999999999.,99999999999999999.},
  {4./3.,-1.,999999999999999.},
  {1./3.,-1./2.,0.}
};

SmoothMatrix::SmoothMatrix(int n1, int n2, double dU, double dV) {
  K_ = 4;  
  n1_ = n1;
  n2_ = n2;
  alphaU_ = 1./dU; 
  alphaU3_ = alphaU_*alphaU_*alphaU_;

  alphaV_ = 1./dV; 
  alphaV3_ = alphaV_*alphaV_*alphaV_;
}

int SmoothMatrix::cutOff(int ij, int rs, int n1n2) const {
  


#ifdef DEBUG
  if (n1_ < 6 || n2_ < 6) { 
    cout << "FATAL ERROR, too small grid for cut-off version" << endl;
    exit(-1);
  }
#endif

  int q = 3 - min(ij,rs);
  if (q > 0)
    return q;

  q = max(ij,rs) - (n1n2-4);
  if (q > 0)
    return q;

  return 0;
}

double SmoothMatrix::operator()(int i, int j, int r, int s) const {


  int transU = abs(i-r);
  int transV = abs(j-s);

#if DEBUG
  if (transU >= K_ || transV >= K_) {
    cout << "WARNING: ZERO ELEMENT OF SmoothMatrix (i,j,r,s)= " << i << "," << j << "," << r << "," << s << endl;
    exit(-1);
    return 0.0;
  }
#endif
    

  

  int qU = cutOff(i, r, n1_); 
  int qV = cutOff(j, s, n2_); 
    
  double innerU_d0, innerV_d0; 
  double innerU_d1, innerV_d1; 
  double innerU_d2, innerV_d2; 

  

  if (qU <= transU) {
    innerU_d0 = innerProduct_[0][transU]/alphaU_;
    innerU_d1 = innerProduct_[1][transU]*alphaU_;
    innerU_d2 = innerProduct_[2][transU]*alphaU3_;
  }
  else {
    innerU_d0 = innerProductQd0_[qU-1][transU]/alphaU_;
    innerU_d1 = innerProductQd1_[qU-1][transU]*alphaU_;
    innerU_d2 = innerProductQd2_[qU-1][transU]*alphaU3_;
  }

  if (qV <= transV) {
    innerV_d0 = innerProduct_[0][transV]/alphaV_;  
    innerV_d1 = innerProduct_[1][transV]*alphaV_;  
    innerV_d2 = innerProduct_[2][transV]*alphaV3_; 
  }
  else {
    innerV_d0 = innerProductQd0_[qV-1][transV]/alphaV_;
    innerV_d1 = innerProductQd1_[qV-1][transV]*alphaV_;
    innerV_d2 = innerProductQd2_[qV-1][transV]*alphaV3_;
  }


  return innerU_d2 * innerV_d0 + 2.0*innerU_d1 * innerV_d1 + innerU_d0 * innerV_d2;

}

double SmoothMatrix::norm_l2() const {
  double sum2 = 0.0;
  int i,j,r,s;
  int L_ = K_;

  for(j = 0; j < n2_; j++) {
    int jmin = max(0,j-L_+1);
    int jmax = min(n2_-1,j+L_-1);
    for(i = 0; i < n1_; i++) {
      int imin = max(0,i-K_+1);
      int imax = min(n1_-1,i+K_-1);
      for(s = jmin; s <= jmax; s++) {
        for(r = imin; r <= imax; r++) {
          double t = operator()(i,j,r,s);
          sum2 += t*t;
        }
      }
    }
  }
  return sqrt(sum2);
}
 
