/* Copyright (c) 1997-2018
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.polymake.org

   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; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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.
--------------------------------------------------------------------------------
*/

#ifndef __GROUP_ISOTYPIC_COMPONENTS_H
#define __GROUP_ISOTYPIC_COMPONENTS_H

#include "polymake/Map.h"
#include "polymake/Matrix.h"
#include "polymake/IncidenceMatrix.h"
#include "polymake/ListMatrix.h"
#include "polymake/SparseVector.h"
#include "polymake/linalg.h"
#include "polymake/group/orbit.h"
#include "polymake/group/group_tools.h"

namespace polymake { namespace group {

template<typename Perm>
SparseMatrix<Rational> permutation_matrix(const Perm& perm,
                                          const Array<int>& coordinate_permutation)
{
   SparseMatrix<Rational> permutation_matrix(degree(perm), degree(perm));
   int i(0);
   for (auto pit = entire(perm); !pit.at_end(); ++pit, ++i)
      permutation_matrix(coordinate_permutation[*pit],
                         coordinate_permutation[i]) = 1;
   return permutation_matrix;
}

template<typename Perm>
int inverse_perm_at(const Perm& perm,
                    int k)
{
   assert(k < perm.size());
   int i(0);
   for (auto pit = entire(perm); !pit.at_end(); ++pit, ++i)
      if (*pit == k)
         return i;
   std::ostringstream msg;
   wrap(msg) << "The array " << perm << " is not a permutation.";
   throw std::runtime_error(msg.str());
}

   /*
template<typename InducedAction, typename RowType>
SparseMatrix<Rational> 
isotypic_projector_impl(const RowType& character,
                        const InducedAction& induced_action,
                        int degree,
                        const ConjugacyClasses& conjugacy_classes,
                        int order)
{
   SparseMatrix<Rational> isotypic_projector(degree, degree);
   for (int i=0; i<conjugacy_classes.size(); ++i) {
      for (Entire<ConjugacyClass>::const_iterator cit = entire(conjugacy_classes[i]); !cit.at_end(); ++cit) {
         isotypic_projector += 
            character[i] // FIXME: conjugate here, once complex character tables are implemented
            * induced_action.induced_rep(*cit);
      }
   }

   //    chi(G.identity())/G.order() * sum([chi(g).conjugate() * rep.rho(g) for g in G])
   return isotypic_projector * character[0] / order;
}
   */

namespace {

template<typename E>   
auto matrix_rep(const Array<E>& g, const Array<int>& permutation_to_orbit_order)
{
   return permutation_matrix(g, permutation_to_orbit_order);
}
   
template<typename Scalar>
auto matrix_rep(const Matrix<Scalar>& g, const Array<int>&)
{
   return g;
}
   
} // end anonymous namespace

/*
    Implement the projector to the isotypic component given by the character //chi// using the formula

    pi_chi = chi(G.identity())/G.order() * sum([chi(g).conjugate() * rep.rho(g) for g in G])
 */
template<typename RowType, typename Element>
SparseMatrix<CharacterNumberType> 
isotypic_projector_impl(const RowType& chi,
                        const ConjugacyClasses<Element>& conjugacy_classes,
                        const Array<int>& permutation_to_orbit_order,
                        int group_order)
{
   const int deg(degree(conjugacy_classes[0][0]));
   SparseMatrix<CharacterNumberType> isotypic_projector(deg, deg);
   for (int i=0; i<conjugacy_classes.size(); ++i) {
      if (is_zero(chi[i])) continue;
      for (auto cit = entire(conjugacy_classes[i]); !cit.at_end(); ++cit) {
         isotypic_projector += 
            chi[i] // FIXME: conjugate here, once complex character tables are implemented
            * matrix_rep(*cit, permutation_to_orbit_order);
      }
   }
   isotypic_projector *= chi[0] / group_order;
   return isotypic_projector;
}

template<typename InducedAction, typename RowType, typename Element>
ListMatrix<SparseVector<CharacterNumberType> >
isotypic_basis_impl(const RowType& character,
                    const InducedAction& induced_action,
                    int degree,
                    const ConjugacyClasses<Element>& conjugacy_classes,
                    int order)
{
   ListMatrix<SparseVector<CharacterNumberType>>
      isotypic_basis(0, degree),
      kernel_so_far(unit_matrix<CharacterNumberType>(degree));
   // we fill the matrix row-wise. The entire matrix is
   //    chi(G.identity())/G.order() * sum([chi(g).conjugate() * rep.rho(g) for g in G])
   // and rep.rho(g) (k, g^{-1}(k)) = 1.
   for (int k=0; k<degree; ++k) {
      SparseVector<CharacterNumberType> new_row(degree);
      for (int i=0; i<conjugacy_classes.size(); ++i) {
         for (auto cit = entire(conjugacy_classes[i]); !cit.at_end(); ++cit) {
            for (int j=0; j<degree; ++j)
               if (induced_action.index_of_inverse_image(*cit, j) == k) {
                  new_row[j] += 
                     character[i]; // FIXME: conjugate here, once complex character tables are implemented
               }
         }
      }
      add_row_if_rowspace_increases(isotypic_basis, new_row, kernel_so_far);
   }

   return isotypic_basis * character[0] / order;
}

template<typename RowType, typename Element>
ListMatrix<SparseVector<CharacterNumberType> >
isotypic_basis_impl(const RowType& character,
                    const ConjugacyClasses<Element>& conjugacy_classes,
                    const Array<int>& permutation_to_orbit_order,
                    int order)
{
   const int deg(degree(conjugacy_classes[0][0]));
   ListMatrix<SparseVector<CharacterNumberType>>
      isotypic_basis(0, deg),
      kernel_so_far(unit_matrix<CharacterNumberType>(deg));
   // we fill the matrix row-wise. The entire matrix is
   //    chi(G.identity())/G.order() * sum([chi(g).conjugate() * rep.rho(g) for g in G])
   // and rep.rho(g) (k, g^{-1}(k)) = 1.
   for (int k=0; k<deg; ++k) {
      SparseVector<CharacterNumberType> new_row(deg);
      for (int i=0; i<conjugacy_classes.size(); ++i) {
         if (is_zero(character[i])) continue;
         for (auto cit = entire(conjugacy_classes[i]); !cit.at_end(); ++cit) {
            new_row[permutation_to_orbit_order[inverse_perm_at(*cit,k)]] += 
               character[i]; // FIXME: conjugate here, once complex character tables are implemented
         }
      }
      add_row_if_rowspace_increases(isotypic_basis, new_row, kernel_so_far);
   }

   return isotypic_basis * (character[0] / order);
}


template<typename SparseMatrixType, typename Element>
IncidenceMatrix<> 
isotypic_supports_impl(const SparseMatrixType& S,
                       const Matrix<CharacterNumberType>& character_table,
                       const ConjugacyClasses<Element>& conjugacy_classes,
                       const Array<int>& permutation_to_orbit_order,
                       int order)
{
   const int n_irreps = character_table.rows();
   IncidenceMatrix<> supp(S.rows(), n_irreps);
   for (int i=0; i<n_irreps; ++i) {
      const SparseMatrix<CharacterNumberType> image = isotypic_projector_impl(character_table[i], conjugacy_classes, permutation_to_orbit_order, order) * T(S);
      int j(0);
      for (auto cit = entire(cols(image)); !cit.at_end(); ++cit, ++j)
         if (!is_zero(*cit))
            supp(j,i) = 1;
   }
   return supp;
}

} }

#endif // __GROUP_ISOTYPIC_COMPONENTS_H


// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:

