/* sfscale.cpp: structure factor anisotropic scaling implementation */
//C Copyright (C) 2000-2006 Kevin Cowtan and University of York
//L
//L  This library is free software and is distributed under the terms
//L  and conditions of version 2.1 of the GNU Lesser General Public
//L  Licence (LGPL) with the following additional clause:
//L
//L     `You may also combine or link a "work that uses the Library" to
//L     produce a work containing portions of the Library, and distribute
//L     that work under terms of your choice, provided that you give
//L     prominent notice with each copy of the work that the specified
//L     version of the Library is used in it, and that you include or
//L     provide public access to the complete corresponding
//L     machine-readable source code for the Library including whatever
//L     changes were used in the work. (i.e. If you make changes to the
//L     Library you must distribute those, but you do not need to
//L     distribute source or object code to those portions of the work
//L     not covered by this licence.)'
//L
//L  Note that this clause grants an additional right and does not impose
//L  any additional restriction, and so does not affect compatibility
//L  with the GNU General Public Licence (GPL). If you wish to negotiate
//L  other terms, please contact the maintainer.
//L
//L  You can redistribute it and/or modify the library under the terms of
//L  the GNU Lesser General Public License as published by the Free Software
//L  Foundation; either version 2.1 of the License, or (at your option) any
//L  later version.
//L
//L  This library is distributed in the hope that it will be useful, but
//L  WITHOUT ANY WARRANTY; without even the implied warranty of
//L  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//L  Lesser General Public License for more details.
//L
//L  You should have received a copy of the CCP4 licence and/or GNU
//L  Lesser General Public License along with this library; if not, write
//L  to the CCP4 Secretary, Daresbury Laboratory, Warrington WA4 4AD, UK.
//L  The GNU Lesser General Public can also be obtained by writing to the
//L  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//L  MA 02111-1307 USA


#include "sfscale.h"
#include "../core/resol_targetfn.h"
#include "../core/hkl_compute.h"


namespace clipper {

template<class T> bool SFscale_aniso<T>::operator() ( HKL_data<datatypes::F_sigF<T> >& fo, const HKL_data<datatypes::F_phi<T> >& fc )
{
  typedef HKL_info::HKL_reference_index HRI;
  // expand to P1 in order to preserve symmetry
  const HKL_info& hkls = fo.hkl_info();
  Spacegroup spgrp1( Spacegroup::P1 );
  HKL_info hkl1( spgrp1, hkls.cell(), hkls.resolution(), true );
  HKL_data<datatypes::F_sigF<T> > fo1( hkl1 );
  HKL_data<datatypes::F_phi<T> >  fc1( hkl1 );
  for ( HRI ih = hkl1.first(); !ih.last(); ih.next() ) {
    datatypes::F_sigF<T> f = fo[ih.hkl()];
    if ( f.f() >= nsig_ * f.sigf() ) {
      fo1[ih] = f;
      fc1[ih] = fc[ih.hkl()];
    }
  }
  // do the aniso scaling
  std::vector<double> param( 7, 0.0 );
  BasisFn_log_aniso_gaussian bfn;
  TargetFn_scaleLogF1F2<datatypes::F_sigF<T>,datatypes::F_phi<T> >
    tfn( fo1, fc1 );
  ResolutionFn rfn( hkl1, bfn, tfn, param );
  for ( HRI ih = hkls.first(); !ih.last(); ih.next() )
    if ( !fo[ih].missing() )
      fo[ih].scale( exp( 0.5*bfn.f(ih.hkl(),hkls.cell(),rfn.params()) ) );
  u_i = bfn.u_aniso_orth( rfn.params() );
  u_f = 0.5 * u_i;
  return true;
}

template<class T> bool SFscale_aniso<T>::operator() ( HKL_data<datatypes::F_phi<T> >& fc, const HKL_data<datatypes::F_sigF<T> >& fo )
{
  typedef HKL_info::HKL_reference_index HRI;
  // expand to P1 in order to preserve symmetry
  const HKL_info& hkls = fo.hkl_info();
  Spacegroup spgrp1( Spacegroup::P1 );
  HKL_info hkl1( spgrp1, hkls.cell(), hkls.resolution(), true );
  HKL_data<datatypes::F_sigF<T> > fo1( hkl1 );
  HKL_data<datatypes::F_phi<T> >  fc1( hkl1 );
  for ( HRI ih = hkl1.first(); !ih.last(); ih.next() ) {
    datatypes::F_sigF<T> f = fo[ih.hkl()];
    if ( f.f() >= nsig_ * f.sigf() ) {
      fo1[ih] = f;
      fc1[ih] = fc[ih.hkl()];
    }
  }
  // do the aniso scaling
  std::vector<double> param( 7, 0.0 );
  BasisFn_log_aniso_gaussian bfn;
  TargetFn_scaleLogF1F2<datatypes::F_phi<T>,datatypes::F_sigF<T> >
    tfn( fc1, fo1 );
  ResolutionFn rfn( hkl1, bfn, tfn, param );
  for ( HRI ih = hkls.first(); !ih.last(); ih.next() )
    if ( !fc[ih].missing() )
      fc[ih].scale( exp( 0.5*bfn.f(ih.hkl(),hkls.cell(),rfn.params()) ) );
  u_i = bfn.u_aniso_orth( rfn.params() );
  u_f = 0.5 * u_i;
  return true;
}

template<class T> bool SFscale_aniso<T>::operator() ( HKL_data<datatypes::F_sigF<T> >& fo )
{
  typedef HKL_info::HKL_reference_index HRI;
  // expand to P1 in order to preserve symmetry
  const HKL_info& hkls = fo.hkl_info();
  Spacegroup spgrp1( Spacegroup::P1 );
  HKL_info hkl1( spgrp1, hkls.cell(), hkls.resolution(), true );
  HKL_data<datatypes::F_sigF<T> > fo1( hkl1 ), fs1( hkl1 ), fc1( hkl1 );
  for ( HRI ih = hkl1.first(); !ih.last(); ih.next() ) {
    datatypes::F_sigF<T> f = fo[ih.hkl()];
    if ( f.f() >= nsig_ * f.sigf() ) fo1[ih] = f;
  }

  /*
  // perform aniso scaling 3 times to allow aniso scale from previous
  // cycle to correct for missing data on the next cycle:
  // start with unscaled data and iterate
  BasisFn_log_aniso_gaussian bfn;
  std::vector<ftype> param( 7, 0.0 ), params( 12, 1.0 );
  for ( int c = 0; c < 3; c++ ) {
    // create artificial F's from mean F with resolution
    TargetFn_meanFnth<datatypes::F_sigF<T> > tfns( fs1, 2.0 );
    BasisFn_spline bfns( fs1, 12 );
    ResolutionFn rfns( hkl1, bfns, tfns, params );
    for ( HRI ih = hkl1.first(); !ih.last(); ih.next() )
      fc1[ih] = datatypes::F_sigF<T>( sqrt(rfns.f(ih)), 1.0 );

    // do the aniso scaling
    TargetFn_scaleLogF1F2<datatypes::F_sigF<T>,datatypes::F_sigF<T> >
      tfn( fo1, fc1 );
    ResolutionFn rfn( hkl1, bfn, tfn, param );
    param = rfn.params();
    // set trace to zero (i.e. no isotropic correction)
    ftype dp = (param[1]+param[2]+param[3])/3.0;
    param[1] -= dp;
    param[2] -= dp;
    param[3] -= dp;
    // create new scaled list correct with current estimate
    fs1 = fo1;
    for ( HRI ih = hkl1.first(); !ih.last(); ih.next() )
      if ( !fs1[ih].missing() )
	fs1[ih].scale( exp( 0.5*bfn.f(ih.hkl(),hkl1.cell(),param) ) );
    u = 0.5 * bfn.u_aniso_orth( param );
    //std::cout << c << " | " << param[1] << " " << param[2] << " " << param[3] << " " << param[4] << " " << param[5] << " " << param[6] << "\n";
  }
  */

  // calc target values
  for ( HRI ih = fc1.first(); !ih.last(); ih.next() )
    fc1[ih] = datatypes::F_sigF<T>( sqrt( ih.hkl_class().epsilon() ), 1.0 );

  // do correction
  u_i = u_f = U_aniso_orth( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 );
  std::vector<ftype> param( 7, 0.0 );
  BasisFn_log_aniso_gaussian bfn;
  ftype dp;
  for ( int c = 0; c < 2; c++ ) {
    // remove current anistropy estimate
    datatypes::Compute_scale_u_aniso<datatypes::F_sigF<T> > compute_s(1.0,-u_f);
    fs1.compute( fo1, compute_s );

    // and calc E scale
    const int npar_scl = 12;
    HKL_data<datatypes::E_sigE<T> > eo( hkl1 );
    datatypes::Compute_EsigE_from_FsigF<T> compute_e;
    eo.compute( fs1, compute_e );
    BasisFn_spline basissc( eo, npar_scl, 1.0 );
    std::vector<ftype> param_fo( basissc.num_params(), 1.0 );
    TargetFn_scaleEsq<datatypes::E_sigE<T> > tfn_fo( eo );
    ResolutionFn rfn_fo( hkl1, basissc, tfn_fo, param_fo );
    param_fo = rfn_fo.params();

    // prescale F to E-like scale
    fs1 = fo1;
    for ( HRI ih = fs1.first(); !ih.last(); ih.next() )
      fs1[ih].scale( sqrt( basissc.f_s( ih.invresolsq(), param_fo ) ) );

    // do the aniso scaling
    TargetFn_scaleLogF1F2<datatypes::F_sigF<T>,datatypes::F_sigF<T> >
      tfn( fs1, fc1 );
    param = std::vector<ftype>( 7, 0.0 );
    ResolutionFn rfn( hkl1, bfn, tfn, param );
    param = rfn.params();
    // set trace to zero (i.e. no isotropic correction)
    dp = (param[1]+param[2]+param[3])/3.0;
    param[1] -= dp; param[2] -= dp; param[3] -= dp;
    u_i = bfn.u_aniso_orth( param );
    u_f = 0.5 * u_i;
    //std::cout << c << " | " << param[1] << " " << param[2] << " " << param[3] << " " << param[4] << " " << param[5] << " " << param[6] << "\n"; std::cout << " DP " << dp << "\n";
  }

  // sharpen or smooth as required
  Matrix<ftype> m(3,3);
  m(0,0)=       param[1]; m(1,1)=       param[2]; m(2,2)=       param[3];
  m(0,1)=m(1,0)=param[4]; m(0,2)=m(2,0)=param[5]; m(1,2)=m(2,1)=param[6];
  std::vector<ftype> ev = m.eigen();
  //std::cout << "EIGEN " << param[1] << " " << param[2] << " " << param[3] << " " << ev[0] << " " << ev[1] << " " << ev[2] << std::endl;
  dp = 0.0;
  if ( mode_ == SHARPEN   ) dp = ev[2];
  if ( mode_ == UNSHARPEN ) dp = ev[0];
  param[1] -= dp; param[2] -= dp; param[3] -= dp;
  u_i = bfn.u_aniso_orth( param );
  u_f = 0.5 * u_i;

  // store the results
  datatypes::Compute_scale_u_aniso<datatypes::F_sigF<T> > compute_s(1.0,-u_f);
  fo.compute( fo, compute_s );

  return true;
}


template<class T> const U_aniso_orth& SFscale_aniso<T>::u_aniso_orth( TYPE t ) const
{
  if ( t == I ) return u_i;
  else          return u_f;
}


// compile templates

template class SFscale_aniso<ftype32>;

template class SFscale_aniso<ftype64>;


} // namespace clipper
