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

#include "polymake/client.h"
#include "polymake/graph/Lattice.h"
#include "polymake/graph/Decoration.h"

namespace polymake { namespace graph { namespace lattice {

   template <>
      void InverseRankMap<Nonsequential>::set_rank(int n, int r) {
         inverse_rank_map[r].push_back(n);
      }

   template <>
      void InverseRankMap<Sequential>::set_rank(int n, int r) {
         auto p = inverse_rank_map.find(r);
         if(p.at_end())
            inverse_rank_map[r] = std::make_pair(n,n);
         else {
            p->second.first= std::min(p->second.first,n);
            p->second.second = std::max(p->second.second,n);
         }
      }

   template <>
      void InverseRankMap<Nonsequential>::delete_node_and_squeeze(int n, int r) {
         Nonsequential::nodes_of_rank_type& rlist = inverse_rank_map[r];
         rlist.remove(n);
         if(rlist.size() == 0) inverse_rank_map.erase(r);
         for(auto& kv_pair : inverse_rank_map) {
            for(auto& n_index : kv_pair.second) {
               if(n_index > n) n_index--;
            }
         }
      }

   template <>
      void InverseRankMap<Sequential>::delete_node_and_squeeze(int n, int r) {
         for(auto& kv_pair : inverse_rank_map) {
            if(kv_pair.second.first > n) kv_pair.second.first--;
            if(kv_pair.second.second >= n) kv_pair.second.second--;
            if(kv_pair.second.second < kv_pair.second.first) inverse_rank_map.erase(r);
         }
      }

   template <>
      Nonsequential::nodes_of_rank_ref_type InverseRankMap<Nonsequential>::nodes_of_rank(int d) const {
         auto p = inverse_rank_map.find(d);
         if(p.at_end()) {
            static const Nonsequential::nodes_of_rank_type empty_list;
            return empty_list;
         }
         return p->second;
      }

   template <>
      Sequential::nodes_of_rank_ref_type InverseRankMap<Sequential>::nodes_of_rank(int d) const {
         auto p = inverse_rank_map.find(d);
         if(p.at_end()) {
            static const Sequential::nodes_of_rank_type empty_list;
            return empty_list;
         }
         return Sequential::map_value_as_container(p->second);
      }

   template <>
      const Nonsequential::nodes_of_rank_type InverseRankMap<Nonsequential>::nodes_of_rank_range(int d1, int d2) const {
         Nonsequential::nodes_of_rank_type result;
         if(d2 < d1) return nodes_of_rank_range(d2,d1);
         auto mpval = inverse_rank_map.find_nearest(d1, operations::ge());
         while(!mpval.at_end() && mpval->first <= d2) {
            result.insert(result.end(), mpval->second.begin(), mpval->second.end());
            ++mpval;
         }
         return result;
      }

   template <>
      const Sequential::nodes_of_rank_type InverseRankMap<Sequential>::nodes_of_rank_range(int d1, int d2) const {
         if(d2 < d1) return nodes_of_rank_range(d2,d1);
         auto lower_seq = inverse_rank_map.find_nearest(d1, operations::ge());
         auto upper_seq = inverse_rank_map.find_nearest(d2, operations::le());
         if(!lower_seq.at_end() && !upper_seq.at_end()) {
            int lower_bound = std::min( lower_seq->second.first, upper_seq->second.first);
            int upper_bound = std::max( lower_seq->second.second, upper_seq->second.second);
            return Sequential::nodes_of_rank_type(lower_bound, upper_bound-lower_bound+1);
         }
         return Sequential::nodes_of_rank_type();
      }


}}}
