// GenFem - A high-level finite element library
// Copyright (C) 2010-2026 Eric Bechet
//
// See the LICENSE file for license information and contributions.
// Please report all bugs and problems to <bechet@cadxfem.org>.
//
// Initial design: Eric Bechet (rev.1379)


#ifndef _GEN_PLUS_OPERATOR__H_
#define _GEN_PLUS_OPERATOR__H_


template<class T,int n> class  PlusGenTerm : public genTerm<T,n>
{
public :
  typedef typename TensorialTraits<T>::ValType ValType;
  typedef typename ContainerTraits<ValType,n>::Container ContainerValType;
  static const int Nsp=n;
protected :
  typename genTerm<T,n>::ConstHandle gt1;
  typename genTerm<T,n>::ConstHandle gt2;
  bool samespace[2];
 
public:
  PlusGenTerm(const typename genTerm<T,n>::ConstHandle &gt1_, const typename genTerm<T,n>::ConstHandle &gt2_) : gt1(gt1_),gt2(gt2_)
  {
    bool k=true;
    for (int i=0;i<n;++i)
    {
      samespace[i]=(gt1->getIncidentSpaceTag(i)==gt2->getIncidentSpaceTag(i));
      k=k&&(!samespace[i]);
    }
    if ((n>1)&&(k)) std::cerr << "Warning / performance issue : concatenation of incompatible bilinear forms" << std::endl;
  }
  virtual int getNumKeys(MElement* ele,int k=0) const
  {
    if (n==0) return 0;
      else
      if (samespace[k]) return gt1->getNumKeys(ele,k);
        else return gt1->getNumKeys(ele,k)+gt2->getNumKeys(ele,k);
  }
  virtual void getKeys(MElement* ele, std::vector<Dof> &keys, int k=0) const
  {
    if (n==0) return ;
      else
      if (samespace[k]) gt1->getKeys(ele,keys,k);
        else
        {
          gt1->getKeys(ele,keys,k);
          std::vector<Dof> keys2;
          gt2->getKeys(ele,keys2,k);
          keys.insert( keys.end(), keys2.begin(), keys2.end() );
        }
  }
  virtual int getIncidentSpaceTag(int k=0) const
  {
    if (n==0) return gt1->getIncidentSpaceTag(k);
      else
    if (samespace[k]) return gt1->getIncidentSpaceTag(k);
        else return gt1->getIncidentSpaceTag(k)+gt2->getIncidentSpaceTag(k)*1000; // to be changed
  }
  virtual void get(MElement* ele, int npts, IntPt* GP, std::vector<ContainerValType> &vvals) const;
//  virtual void get(MElement* ele, int npts, IntPt* GP, ContainerValType &vals) const;
  virtual PlusGenTerm<T,n>* clone () const { return new PlusGenTerm<T,n>(gt1,gt2);}
};


template<class T,int n> class  MinusGenTerm : public genTerm<T,n>
{
public :
  typedef typename TensorialTraits<T>::ValType ValType;
  typedef typename ContainerTraits<ValType,n>::Container ContainerValType;
  static const int Nsp=n;
protected :
  typename genTerm<T,n>::ConstHandle gt1;
  typename genTerm<T,n>::ConstHandle gt2;
  bool samespace[2];
 
public:
  MinusGenTerm(const typename genTerm<T,n>::ConstHandle &gt1_, const typename genTerm<T,n>::ConstHandle &gt2_) : gt1(gt1_),gt2(gt2_)
  {
    bool k=true;
    for (int i=0;i<n;++i)
    {
      samespace[i]=(gt1->getIncidentSpaceTag(i)==gt2->getIncidentSpaceTag(i));
      k=k&&(!samespace[i]);
    }
    if ((n>1)&&(k)) std::cerr << "Warning / performance issue : concatenation of incompatible bilinear forms" << std::endl;
  }
  virtual int getNumKeys(MElement* ele,int k=0) const
  {
    if (n==0) return 0;
      else
      if (samespace[k]) return gt1->getNumKeys(ele,k);
        else return gt1->getNumKeys(ele,k)+gt2->getNumKeys(ele,k);
  }
  virtual void getKeys(MElement* ele, std::vector<Dof> &keys, int k=0) const
  {
    if (n==0) return ;
      else
      if (samespace[k]) gt1->getKeys(ele,keys,k);
        else
        {
          gt1->getKeys(ele,keys,k);
          std::vector<Dof> keys2;
          gt2->getKeys(ele,keys2,k);
          keys.insert( keys.end(), keys2.begin(), keys2.end() );
        }
  }
  virtual int getIncidentSpaceTag(int k=0) const
  {
    if (n==0) return gt1->getIncidentSpaceTag(k);
      else
    if (samespace[k]) return gt1->getIncidentSpaceTag(k);
        else return gt1->getIncidentSpaceTag(k)+gt2->getIncidentSpaceTag(k)*1000; // to be changed
  }
  virtual void get(MElement* ele, int npts, IntPt* GP, std::vector<ContainerValType> &vvals) const;
//  virtual void get(MElement* ele, int npts, IntPt* GP, ContainerValType &vals) const;
  virtual MinusGenTerm<T,n>* clone () const { return new MinusGenTerm<T,n>(gt1,gt2);}
};



template<class GT1,class GT2> std::shared_ptr<genTerm<typename GT1::ValType,GT1::Nsp> >
operator+(const std::shared_ptr<GT1> &gt1, const std::shared_ptr<GT2> &gt2)
{
  return std::shared_ptr<genTerm<typename GT1::ValType,GT1::Nsp> > (new PlusGenTerm<typename GT1::ValType,GT1::Nsp>(gt1,gt2));
}

template<class GT1,class GT2> std::shared_ptr<genTerm<typename GT1::ValType,GT1::Nsp> >
operator-(const std::shared_ptr<GT1> &gt1, const std::shared_ptr<GT2> &gt2)
{
  return std::shared_ptr<genTerm<typename GT1::ValType,GT1::Nsp> > (new MinusGenTerm<typename GT1::ValType,GT1::Nsp>(gt1,gt2));
}




//--------------------------------------------------------------------------
// Get
//--------------------------------------------------------------------------


template<class T,int n> void PlusGenTerm<T,n>::get(MElement* ele, int npts, IntPt* GP, std::vector<ContainerValType> &vvals) const
{
  bool k=true;
  for (int i=0;i<n;++i) k=k&&samespace[i];
  if (k)
  {
    std::vector<ContainerValType> vvals2(npts);
    gt1->get(ele,npts,GP,vvals);
    gt2->get(ele,npts,GP,vvals2);
    for (int i=0;i<npts;++i)
      Apply(vvals[i],vvals2[i],vvals[i],PlusOp<ValType>());
  }
  else
  {
    std::vector<ContainerValType> vvals1(npts);
    std::vector<ContainerValType> vvals2(npts);
    gt1->get(ele,npts,GP,vvals1);
    gt2->get(ele,npts,GP,vvals2);
    for (int i=0;i<npts;++i)
      Cat(vvals1[i],vvals2[i],vvals[i],samespace); // concatenate
  }
}

template<class T,int n> void MinusGenTerm<T,n>::get(MElement* ele, int npts, IntPt* GP, std::vector<ContainerValType> &vvals) const
{
  bool k=true;
  for (int i=0;i<n;++i) k=k&&samespace[i];
  if (k)
  {
    std::vector<ContainerValType> vvals2(npts);
    gt1->get(ele,npts,GP,vvals);
    gt2->get(ele,npts,GP,vvals2);
    for (int i=0;i<npts;++i)
      Apply(vvals[i],vvals2[i],vvals[i],MinusOp<ValType>());
  }
  else
  {
    std::vector<ContainerValType> vvals1(npts);
    std::vector<ContainerValType> vvals2(npts);
    gt1->get(ele,npts,GP,vvals1);
    gt2->get(ele,npts,GP,vvals2);
    for (int i=0;i<npts;++i)
      Catm(vvals1[i],vvals2[i],vvals[i],samespace); // concatenate
  }
}


#endif // _GEN_PLUS_OPERATOR__H_