//=======================================================================
// Boost.Graph library vf2_sub_graph_iso test 2
// Test of return value and behaviour with empty graphs
//
// Copyright (C) 2013 Jakob Lykke Andersen, University of Southern Denmark (jlandersen@imada.sdu.dk)
//
// 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)
//=======================================================================

#include <iostream>
#include <boost/test/minimal.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>

struct test_callback {
  test_callback(bool &got_hit, bool stop) : got_hit(got_hit), stop(stop) { }

  template<typename Map1To2, typename Map2To1>
  bool operator()(Map1To2 map1to2, Map2To1 map2to1) {
    got_hit = true;
    return stop;
  }

  bool &got_hit;
  bool stop;
};

struct false_predicate {
  template<typename VertexOrEdge1, typename VertexOrEdge2>
  bool operator()(VertexOrEdge1 ve1, VertexOrEdge2 ve2) const {
    return false;
  }
};

void test_empty_graph_cases() {
  typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> Graph;
  Graph gEmpty, gLarge;
  add_vertex(gLarge);

  { // isomorphism
    bool got_hit = false;
    test_callback callback(got_hit, true);
    bool exists = vf2_graph_iso(gEmpty, gEmpty, callback);
    BOOST_CHECK(exists);
    BOOST_CHECK(got_hit); // even empty matches are reported
  }
  { // subgraph isomorphism (induced)
    { // empty vs. empty
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_iso(gEmpty, gEmpty, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit); // even empty matches are reported
    }
    { // empty vs. non-empty
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_iso(gEmpty, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit); // even empty matches are reported
    }
  }
  { // subgraph monomorphism (non-induced subgraph isomorphism)
    { // empty vs. empty
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_mono(gEmpty, gEmpty, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit); // even empty matches are reported
    }
    { // empty vs. non-empty
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_mono(gEmpty, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit); // even empty matches are reported
    }
  }
}

void test_return_value() {
  typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> Graph;
  typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
  Graph gSmall, gLarge;
  add_vertex(gSmall);
  Vertex v1 = add_vertex(gLarge);
  Vertex v2 = add_vertex(gLarge);
  add_edge(v1, v2, gLarge);

  { // isomorphism
    { // no morphism due to sizes
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_graph_iso(gSmall, gLarge, callback);
      BOOST_CHECK(!exists);
      BOOST_CHECK(!got_hit);
    }
    { // no morphism due to vertex mismatches
      bool got_hit = false;
      test_callback callback(got_hit, true);
      false_predicate pred;
      bool exists = vf2_graph_iso(gLarge, gLarge, callback, vertex_order_by_mult(gLarge),
                                  boost::edges_equivalent(pred).vertices_equivalent(pred));
      BOOST_CHECK(!exists);
      BOOST_CHECK(!got_hit);
    }
    { // morphism, find all
      bool got_hit = false;
      test_callback callback(got_hit, false);
      bool exists = vf2_graph_iso(gLarge, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit);
    }
    { // morphism, stop after first hit
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_graph_iso(gLarge, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit);
    }
  }
  { // subgraph isomorphism (induced)
    { // no morphism due to sizes
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_iso(gLarge, gSmall, callback);
      BOOST_CHECK(!exists);
      BOOST_CHECK(!got_hit);
    }
    { // no morphism due to vertex mismatches
      bool got_hit = false;
      test_callback callback(got_hit, true);
      false_predicate pred;
      bool exists = vf2_subgraph_iso(gLarge, gLarge, callback, vertex_order_by_mult(gLarge),
                                  boost::edges_equivalent(pred).vertices_equivalent(pred));
      BOOST_CHECK(!exists);
      BOOST_CHECK(!got_hit);
    }
    { // morphism, find all
      bool got_hit = false;
      test_callback callback(got_hit, false);
      bool exists = vf2_subgraph_iso(gLarge, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit);
    }
    { // morphism, stop after first hit
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_iso(gLarge, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit);
    }
  }
  { // subgraph monomorphism (non-induced subgraph isomorphism)
    { // no morphism due to sizes
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_mono(gLarge, gSmall, callback);
      BOOST_CHECK(!exists);
      BOOST_CHECK(!got_hit);
    }
    { // no morphism due to vertex mismatches
      bool got_hit = false;
      test_callback callback(got_hit, true);
      false_predicate pred;
      bool exists = vf2_subgraph_mono(gLarge, gLarge, callback, vertex_order_by_mult(gLarge),
                                  boost::edges_equivalent(pred).vertices_equivalent(pred));
      BOOST_CHECK(!exists);
      BOOST_CHECK(!got_hit);
    }
    { // morphism, find all
      bool got_hit = false;
      test_callback callback(got_hit, false);
      bool exists = vf2_subgraph_mono(gLarge, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit);
    }
    { // morphism, stop after first hit
      bool got_hit = false;
      test_callback callback(got_hit, true);
      bool exists = vf2_subgraph_mono(gLarge, gLarge, callback);
      BOOST_CHECK(exists);
      BOOST_CHECK(got_hit);
    }
  }
}

int test_main(int argc, char* argv[]) {
  test_empty_graph_cases();
  test_return_value();
  return 0;
}
