//=======================================================================
// Copyright 2013 Maciej Piechotka
// Authors: Maciej Piechotka
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#ifndef BOOST_GRAPH_EDGE_COLORING_HPP
#define BOOST_GRAPH_EDGE_COLORING_HPP

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/properties.hpp>
#include <algorithm>
#include <limits>
#include <vector>

/* This algorithm is to find coloring of an edges

   Reference:

   Misra, J., & Gries, D. (1992). A constructive proof of Vizing's
   theorem. In Information Processing Letters.
*/

namespace boost {
  namespace detail {
    template<typename Graph, typename ColorMap>
    bool
    is_free(const Graph &g,
            ColorMap color,
            typename boost::graph_traits<Graph>::vertex_descriptor u,
            typename boost::property_traits<ColorMap>::value_type free_color)
    {
      typedef typename boost::property_traits<ColorMap>::value_type color_t;
      if (free_color == (std::numeric_limits<color_t>::max)())
        return false;
      BGL_FORALL_OUTEDGES_T(u, e, g, Graph) {
        if (get(color, e) == free_color) {
          return false;
        }
      }
      return true;
    }

    template<typename Graph, typename ColorMap>
    std::vector<typename boost::graph_traits<Graph>::vertex_descriptor>
    maximal_fan(const Graph &g,
                ColorMap color,
                typename boost::graph_traits<Graph>::vertex_descriptor x,
                typename boost::graph_traits<Graph>::vertex_descriptor y)
    {
      typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
      std::vector<vertex_t> fan;
      fan.push_back(y);
      bool extended;
      do {
        extended = false;
        BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
          vertex_t v = target(e, g);
          if (is_free(g, color, fan.back(), get(color, e)) &&
              std::find(fan.begin(), fan.end(), v) == fan.end()) {
            fan.push_back(v);
            extended = true;
          }
        }
      } while(extended);
      return fan;
    }
    template<typename Graph, typename ColorMap>
    typename boost::property_traits<ColorMap>::value_type
    find_free_color(const Graph &g,
                    ColorMap color,
                    typename boost::graph_traits<Graph>::vertex_descriptor u)
    {
      typename boost::property_traits<ColorMap>::value_type c = 0;
      while (!is_free(g, color, u, c)) c++;
      return c;
    }

    template<typename Graph, typename ColorMap>
    void
    invert_cd_path(const Graph &g,
                   ColorMap color,
                   typename boost::graph_traits<Graph>::vertex_descriptor x,
                   typename boost::graph_traits<Graph>::edge_descriptor eold,
                   typename boost::property_traits<ColorMap>::value_type c,
                   typename boost::property_traits<ColorMap>::value_type d)
    {
      put(color, eold, d);
      BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
        if (get(color, e) == d && e != eold) {
          invert_cd_path(g, color, target(e, g), e, d, c);
          return;
        }
      }
    }

    template<typename Graph, typename ColorMap>
    void
    invert_cd_path(const Graph &g,
                   ColorMap color,
                   typename boost::graph_traits<Graph>::vertex_descriptor x,
                   typename boost::property_traits<ColorMap>::value_type c,
                   typename boost::property_traits<ColorMap>::value_type d)
    {
      BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
        if (get(color, e) == d) {
          invert_cd_path(g, color, target(e, g), e, d, c);
          return;
        }
      }
    }
    
    template<typename Graph, typename ColorMap, typename ForwardIterator>
    void
    rotate_fan(const Graph &g,
               ColorMap color,
               typename boost::graph_traits<Graph>::vertex_descriptor x,
               ForwardIterator begin,
               ForwardIterator end)
    {
      typedef typename boost::graph_traits<Graph>::edge_descriptor edge_t;
      if (begin == end) {
        return;
      }
      edge_t previous = edge(x, *begin, g).first;
      for (begin++; begin != end; begin++) {
        edge_t current = edge(x, *begin, g).first;
        put(color, previous, get(color, current));
        previous = current;
      }
    }

    template<typename Graph, typename ColorMap>
    class find_free_in_fan
    {
    public:
      find_free_in_fan(const Graph &graph,
                       const ColorMap color,
                       typename boost::property_traits<ColorMap>::value_type d)
        : graph(graph),
          color(color),
          d(d) {}
      bool operator()(const typename boost::graph_traits<Graph>::vertex_descriptor u) const {
        return is_free(graph, color, u, d);
      }
    private:
      const Graph &graph;
      const ColorMap color;
      const typename boost::property_traits<ColorMap>::value_type d;
    };
  }

  template<typename Graph, typename ColorMap>
  typename boost::property_traits<ColorMap>::value_type
  color_edge(const Graph &g,
             ColorMap color,
             typename boost::graph_traits<Graph>::edge_descriptor e)
  {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename boost::property_traits<ColorMap>::value_type color_t;
    typedef typename std::vector<vertex_t>::iterator fan_iterator;
    using namespace detail;
    vertex_t x = source(e, g), y = target(e, g);
    std::vector<vertex_t> fan = maximal_fan(g, color, x, y);
    color_t c = find_free_color(g, color, x);
    color_t d = find_free_color(g, color, fan.back());
    invert_cd_path(g, color, x, c, d);
    fan_iterator w = std::find_if(fan.begin(),
                                  fan.end(),
                                  find_free_in_fan<Graph, ColorMap>(g, color, d));
    rotate_fan(g, color, x, fan.begin(), w + 1);
    put(color, edge(x, *w, g).first, d);
    return (std::max)(c, d);
  }

  template<typename Graph, typename ColorMap>
  typename boost::property_traits<ColorMap>::value_type
  edge_coloring(const Graph &g,
                ColorMap color)
  {
    typedef typename boost::property_traits<ColorMap>::value_type color_t;
    BGL_FORALL_EDGES_T(e, g, Graph) {
      put(color, e, (std::numeric_limits<color_t>::max)());
    }
    color_t colors = 0;
    BGL_FORALL_EDGES_T(e, g, Graph) {
      colors = (std::max)(colors, color_edge(g, color, e) + 1);
    }
    return colors;
  }
}

#endif
