// An example solution to Programming Assignment 3.
//
// CS 306, Computer Algorithms II, Fall 2007


#include <iostream>
#include <cstdlib>
#include <vector>
#include <sstream>


struct guest_name
  std::string first_name, last_name


static const unsigned guest_list_size = 9


typedef std::vector<guest_name> seating_list
typedef seating_list::const_iterator seats_citer


typedef std::vector<std::string> string_list


struct guest
  guest_name name
  string_list interests
  string_list acquaintences


typedef std::vector<guest> guest_list


// Deja vu c++ style.

   static guest_list read_guests(std::istream &)
   static void output_seating(std::ostream &, const seating_list &)
   static seating_list seat_guests(const guest_list &)
   static bool read_next_nonblank_line(std::istream &, std::string &)
   static void oops(const std::string &)
   static string_list read_words(std::istream &)
   static bool member(const std::string &, const string_list &)
   static bool nonempty_intersection(const string_list &, const string_list &)


static bool
compatible(const guest & g1, const guest & g2)

  // Return true iff the given guests are compatible.

  return 
    nonempty_intersection(g1.interests, g2.interests) and 
    member(g1.name.last_name, g2.acquaintences) and
    member(g2.name.last_name, g1.acquaintences)


static bool
compatible_seating(const guest_list & guests)
  
  // Return true iff the given seating list is compatible.

  const unsigned n = guests.size()

  for unsigned i = 0; i < n; i++
    if not compatible(guests[i], guests[(i + 1) % n])
      return false

  return true


static bool
is_blank_line(const std::string & str)

  // Return true iff the given string is blank.

  return str.find_first_not_of(" \t\n") == std::string::npos


int 
main()

  guest_list guests = read_guests(std::cin)

  if guests.size() != guest_list_size
    std::ostringstream oss
    oss << "A guest list of size " << guest_list_size << " expected, "
	<< "a guest list of size " << guests.size() << " entered"
    oops(oss.str())

  output_seating(std::cout, seat_guests(guests))


static bool
member(const std::string & str, const string_list & strings)

  // Return true iff the given string is in the given string list.

  for unsigned i = 0; i < strings.size(); i++
    if strings[i] == str
      return true

  return false


static bool
nonempty_intersection(const string_list & strs1, const string_list & strs2)

  // Return true iff the given string lists have an string in common.

  for unsigned i = 0; i < strs1.size(); i++
    if member(strs1[i], strs2)
      return true

  return false


static void
oops(const std::string & emsg)

  // print the given error message to std::err and and die.

  std::cerr << "! " << emsg << ".\n"
  exit(EXIT_FAILURE)


static void
output_seating(std::ostream & os, const seating_list & seats)

  // Output to the given output stream the given seating list.

  for seats_citer i = seats.begin(); i != seats.end(); i++
    os << ifirst_name << " " << ilast_name << "\n"


static guest
read_guest(const std::string & str)

  // Read a guest from the given string; return the guest read.  Die with an
  // error if anything untoward happens.

  const size_t comma = str.find(',')
  if std::string::npos == comma
    oops("Guest missing a comma between interests and acquaintences")

  std::istringstream iss(str.substr(0, comma))
  guest g

  if not (iss >> g.name.first_name)
    oops("A guest has no name")
  if not (iss >> g.name.last_name)
    oops("A guest has only one name")

  g.interests = read_words(iss)

  iss.clear()
  iss.str(str.substr(comma + 1))
  g.acquaintences = read_words(iss)

  return g


static guest_list
read_guests(std::istream & is)

  // Read a guest list from the given input stream; return the list.  Die with
  // an error message if anything untoward happens.

  guest_list guests
  std::string str

  while read_next_nonblank_line(is, str)
    guests.push_back(read_guest(str))
  
  return guests


static bool
read_next_nonblank_line(std::istream & is, std::string & str)

  // Read the next non-blank line from the given input stream; store the string
  // read in the given reference and return true.  If there are no more
  // non-blank strings to read, return false.

  do
    if not std::getline(is, str)
      return false
  while is_blank_line(str)

  return true


static string_list
read_words(std::istream & is)

  // Read words from the given input stream; return the words read in a string
  // list.

  string_list words
  std::string word

  while is >> word
    words.push_back(word)

  return words


static bool
seat_guests(guest_list guests, unsigned i, seating_list & seats)

  // Permute the elements in guests[i..guests.size()-1] into a compatible
  // seating arrangement.  If successful, store the seating arrangment in the
  // given seating list and return true; otherwise return false (in which case
  // the contents of seats is undefined).

  if guests.size() - 1 == i
    seats.at(i) = guests.at(i).name
    return compatible_seating(guests)

  for unsigned j = i + 1; j < guests.size(); j++
    if seat_guests(guests, i + 1, seats)
      seats.at(i) = guests.at(i).name
      return true
    std::swap(guests[i], guests[j])

  return false


static seating_list
seat_guests(const guest_list & guests)

  // Find an acceptable seating list for the guests in the given list; return
  // the seating list found.  If no acceptable seating list exists, the
  // returned seating list will be empty.

  seating_list seats(guest_list_size)

  return seat_guests(guests, 0, seats) ? seats : seating_list()


// $Log:$


syntax highlighted by Code2HTML, v. 0.9.1