// file      : synth.cpp
// author    : Boris Kolpackov <boris@kolpackov.net>
// copyright : Copyright (c) 2003 Boris Kolpackov
// license   : http://kolpackov.net/license.html

#include <list>
#include <vector>
#include <iostream>

using std::cout;
using std::endl;

typedef std::vector<unsigned> Vector;

bool
operator== (Vector const& a, Vector const& b)
{
  if (a.size () == b.size ())
  {
    for (Vector::const_iterator
           i (a.begin ()), ae (a.end ()),
           j (b.begin ()), be (b.end ());
       i != ae && j != be; ++i, ++j)
    {
      if (*i != *j) return false;
    }

    return true;
  }

  return false;
}

std::ostream&
operator<< (std::ostream& o, Vector const& a)
{
  cout << "(";

  for (Vector::const_iterator b (a.begin ()), i (b), e (a.end ());
       i != e; ++i)
  {
    if (i != b) cout << ", ";
    cout << *i;
  }

  cout << ")";
}

struct Alternative : Vector
{
  Alternative (unsigned a0,
               unsigned a1,
               unsigned a2,
               unsigned a3,
               unsigned a4,
               unsigned a5)
  {
    push_back (a0);
    push_back (a1);
    push_back (a2);
    push_back (a3);
    push_back (a4);
    push_back (a5);
  }
};


typedef Vector Value;

typedef std::list<Alternative> Alternatives;

void
remove (Alternatives& as, Alternative const& a)
{
  for (Alternatives::iterator i (as.begin ()), e (as.end ()); i != e; ++i)
  {
    if (*i == a)
    {
      as.erase (i);
      return;
    }
  }
}

unsigned
c1 (Alternative const& a)
{
  unsigned const& t = a[0];
  unsigned const& p = a[2];

  if (t == 0) return 2;
  if (p == 1) return 1;
  return 0;
}

unsigned
c2 (Alternative const& a)
{
  unsigned const& t = a[0];

  if (t == 0) return 2;
  return 0;
}

unsigned
c3 (Alternative const& a)
{
  unsigned const& c = a[5];

  return c;
}

unsigned
c4 (Alternative const& a)
{
  unsigned const& e = a[1];
  unsigned const& i = a[4];
  unsigned const& c = a[5];

  if ((i == 0 || i == 1) && (c == 0 || c == 1) && e == 0) return 2;
  return 0;
}

unsigned
c5 (Alternative const& a)
{
  unsigned const& e = a[1];
  unsigned const& s = a[3];
  unsigned const& i = a[4];

  unsigned x (0), y (0);

  if (s != 0 && i != 0)
  {
    x = i;

    if (s == e) y = 2;
    else y = 1;
  }

  return x + y;
}

Value
value (Alternative const& a)
{
  Value v;

  v.push_back (c1(a));
  v.push_back (c2(a));
  v.push_back (c3(a));
  v.push_back (c4(a));
  v.push_back (c5(a));

  return v;
}

bool
cmp (Value const& a, Value const& b)
{
  bool r = false;

  for (size_t i = 0; i < a.size (); ++i)
  {
    if (a[i] < b[i]) return false;
    if (a[i] > b[i]) r = true;
  }

  return r;
}

void
optimize (Alternatives& alts)
{
  for (Alternatives::iterator i (alts.begin ()); i != alts.end ();)
  {
    bool advance = true;

    Alternatives::iterator j (i);

    ++j;

    for (; j != alts.end ();)
    {
      if (cmp (value (*i), value (*j)))
      {
        alts.erase (j++);
      }
      else if (cmp (value (*j), value (*i)))
      {
        alts.erase (i++);
        advance = false;
        break;
      }
      else
      {
        ++j;
      }
    }

    if (advance) ++i;
  }
}


int
main ()
{
  Alternatives alts;

  // Generate alternative set.
  //
  for (unsigned t = 0; t < 2; ++t)
  {
    for (unsigned e = 0; e < 2; ++e)
    {
      for (unsigned p = 0; p < 2; ++p)
      {
        for (unsigned s = 0; s < 2; ++s)
        {
          for (unsigned i = 0; i < 3; ++i)
          {
            for (unsigned c = 0; c < 3; ++c)
            {
              Alternative a (t, e, p, s, i, c);
              alts.push_back (a);
            }
          }
        }
      }
    }
  }

  // Remove impossible alternatives that are known to end up
  // in optimized set.
  //

  remove (alts, Alternative (0, 0, 1, 1, 1, 1));
  remove (alts, Alternative (0, 0, 1, 1, 2, 2));
  remove (alts, Alternative (0, 1, 1, 1, 2, 2));
  remove (alts, Alternative (1, 0, 1, 1, 1, 1));
  remove (alts, Alternative (1, 0, 0, 1, 1, 1));
  remove (alts, Alternative (1, 0, 1, 1, 1, 0));
  remove (alts, Alternative (1, 0, 1, 1, 2, 2));
  remove (alts, Alternative (1, 0, 0, 1, 1, 0));
  remove (alts, Alternative (1, 1, 1, 1, 2, 2));
  remove (alts, Alternative (1, 0, 0, 1, 2, 2));
  remove (alts, Alternative (1, 0, 1, 1, 2, 1));
  remove (alts, Alternative (1, 1, 0, 1, 2, 2));

  cout << alts.size () << " alternatives before optimization" << endl;


  // Optimize.
  //
  optimize (alts);

  // Dump results.
  //
  for (Alternatives::iterator i (alts.begin ()), end (alts.end ());
       i != end; ++i)
  {
    cout << *i << "  " << value (*i) << endl;
  }
};
