/* Copyright (c) 1997-2015
   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/FacetList.h"
#include "polymake/list"

namespace pm { namespace fl_internal {

namespace {

// unlink the cell from the column list and destroy it
// @return the predecessor in the column list
inline
cell* delete_cell(cell* c, chunk_allocator& al)
{
   cell* prev=c->facet.prev;
   if (cell* col_next=(c->col.prev->col.next=c->col.next))
      col_next->col.prev=c->col.prev;
   al.reclaim(c);
   return prev;
}

// unlink the cell from the prefix tree,
// destroy it and all predecessors in the facet
inline
void delete_facet_prefix(cell* c, cell* below, chunk_allocator& al)
{
   if ((c->lex.prev->lex.next=below) != NULL)
      below->lex.prev=c->lex.prev;

   const cell* const head=c->head_cell;
   do c=delete_cell(c, al); while (c != head);      
}

inline
void store_cell_copy(cell* orig, cell* copy)
{
   copy->col.prev=orig->col.prev;
   orig->col.prev=copy;
}

inline
cell* cell_copy(cell* c) { return c->col.prev; }

inline
cell* forget_cell_copy(cell* orig)
{
   cell* copy=orig->col.prev;
   orig->col.prev=copy->col.prev;
   return copy;
}

}

// copy constructor creates new cells and links them properly in the row direction.
// column and prefix-tree links are established later, in vertex_list copy constructor
facet::facet(const facet& l, chunk_allocator& al)
   : _size(l._size)
   , id(l.id)
{
   cell* const head=head_cell();
   cell* prev_copy=head;
   if (_size) {
      const cell* end=l.head_cell();
      for (cell* orig=l.cells.next;  orig!=end;  orig=orig->facet.next) {
         cell* copy=new(al.allocate()) cell(head, orig->vertex);
         store_cell_copy(orig, copy);
         prev_copy->facet.next=copy;
         copy->facet.prev=prev_copy;
         prev_copy=copy;
      }
      prev_copy->facet.next=head;
   } else {
      cells.next=head;
   }
   cells.prev=prev_copy;
}

void facet::unlink_cells(chunk_allocator& al)
{
   cell* cur=cells.prev;
   cell* const head=head_cell();
   cell* below=NULL;
   while (cur != head) {
      below=cur->lex.next;
      if (cur->lex.prev != NULL) {
         delete_facet_prefix(cur, below, al);
         return;
      }
      cur=delete_cell(cur, al);
      if (below != NULL) {
         below->lex.prev=NULL;
         break;
      }
   }
   // the lexicographically next facet (pointed by below) must inherit all out-arcs in the prefix tree
   for (;;) {
      assert(cur != head);
      below=below->facet.prev;
      if ((below->lex.next=cur->lex.next) != NULL)
         below->lex.next->lex.prev=below;
      if (cur->lex.prev != NULL) {
         delete_facet_prefix(cur, below, al);
         return;
      }
      cur=delete_cell(cur, al);
   }
}

cell* facet::push_back(int vertex, chunk_allocator& al)
{
   cell* const head=head_cell();
   cell* c=new(al.allocate()) cell(head, vertex);
   c->facet.next=head;
   c->facet.prev=cells.prev;
   cells.prev->facet.next=c;
   cells.prev=c;
   ++_size;
   return c;
}

vertex_list::vertex_list(const vertex_list& l)
   : vertex(l.vertex)
{
   // reminder: facet copy constructor linked the cloned cells to source cells' col.prev
   //           and saved the original pointers in clones' col.prev
   for (cell* orig=l.first_col;  orig != NULL;  orig=orig->col.next) {
      if (orig->lex.next != NULL) {
         cell* copy=cell_copy(orig);
         cell* next_copy=cell_copy(orig->lex.next);
         copy->lex.next=next_copy;
         next_copy->lex.prev=copy;
      }
   }
   if (l.first_lex != NULL) {
      cell* copy=cell_copy(l.first_lex);
      first_lex=copy;
      copy->lex.prev=lex_head_cell();
   } else {
      first_lex=NULL;
   }

   cell* prev_copy=col_head_cell();
   for (cell* orig=l.first_col;  orig != NULL;  orig=orig->col.next) {
      cell* copy=forget_cell_copy(orig);
      prev_copy->col.next=copy;
      copy->col.prev=prev_copy;
      prev_copy=copy;
   }
   prev_copy->col.next=NULL;
}

bool vertex_list::inserter::push(vertex_list& column, cell* newc)
{
   column.push_front(newc);

   if (first_new == NULL) {
      // the starting vertex of a facet
      if (column.first_lex == NULL) {
         // we have the first facet with the given starting vertex: ready
         column.first_lex=newc;
         newc->lex.prev=column.lex_head_cell();
         return true;
      }
      // there are already facets with this starting vertex: keep on comparing
      first_old=last_old=column.first_lex;
      first_new=last_new=newc;
      return false;
   }

   for (;;) {
      cell* next_old=last_old->facet.next;
      if (next_old != last_old->head_cell) {
         if (next_old->vertex == column.vertex) {
            // still equal prefixes: keep on comparing
            last_old=next_old;
            last_new=newc;
            break;
         }
         if (next_old->vertex > column.vertex) {
            // the new facet is lexicographically lower than the old one
            finalize();
            return true;
         }
      }
      if ((first_old=last_old->lex.next) == NULL) {
         // the new facet is lexicographically following the old one and there are no other facets with the same prefix:
         // hang the new facet below the old one and ready
         last_old->lex.next=last_new;
         last_new->lex.prev=last_old;
         return true;
      }
      // proceed with the lexicographically next existing facet
      last_old=first_old;
      first_new=last_new;
   }
   return false;
}

bool vertex_list::inserter::new_facet_ended()
{
   // reject empty and duplicate facets
   if (last_old != NULL && last_old->facet.next != NULL) {
      finalize();
      return true;
   }
   return false;
}

void vertex_list::inserter::finalize()
{
   // the new facet is lexicographically preceding the old one: take over all out-arcs in the prefix tree
   (first_new->lex.prev=first_old->lex.prev)->lex.next=first_new;
   first_old->lex.prev=NULL;
   for (; first_old != last_old;  first_old=first_old->facet.next, first_new=first_new->facet.next) {
      if ((first_new->lex.next=first_old->lex.next) != NULL)
         first_new->lex.next->lex.prev=first_new;
      first_old->lex.next=NULL;
   }
   last_new->lex.next=last_old;
   last_old->lex.prev=last_new;
}

inline
void lex_order_iterator::scan_facet(const cell* cur)
{
   const cell* const head=cur->head_cell;
   while ((cur=cur->facet.next) != head)
      if (cur->lex.next != NULL)
         Q.push_back(cit(cur));
}

lex_order_iterator::lex_order_iterator(const cell* cur)
{
   if (cur != NULL) {
      Q.push_back(cit(cur));
      scan_facet(cur);
   }
}

lex_order_iterator& lex_order_iterator::operator++ ()
{
   do {
      cit& last_it=Q.back();
      ++last_it;
      if (!last_it.at_end()) {
         scan_facet(last_it.operator->());
         break;
      }
      Q.pop_back();
   } while (!Q.empty());

   return *this;
}

void superset_iterator::valid_position()
{
   it_list::iterator start_it=its.begin(), end_it=its.end(), it=start_it;
   if (it->at_end()) {
      cur=NULL;  return;
   }
   cur=it->get_facet();  ++(*it);
   pointer next;
   while (((++it)==end_it ? it=its.begin() : it) != start_it) {
      do {
         if (it->at_end()) {
            cur=NULL;  return;
         }
         next=it->get_facet();  ++(*it);
      } while (next->id > cur->id);
      if (next->id < cur->id) {
         start_it=it;  cur=next;
      }
   }
}

Table::Table(size_t facet_size, int n_vertices)
   : facet_alloc(facet_size)
   , cell_alloc(sizeof(cell))
   , facets(&facet::list_ptrs)
   , columns(col_ruler::construct(n_vertices))
   , _size(0)
   , next_id(0)
{}

Table::Table(const Table& c)
   : facet_alloc(c.facet_alloc.get_object_size())
   , cell_alloc(sizeof(cell))
   , facets(&facet::list_ptrs)
   , _size(c._size)
   , next_id(c.next_id)
{
   for (const_iterator f=c.begin(), fe=c.end();  f != fe;  ++f)
      push_back_facet(new(facet_alloc.allocate()) facet(*f, cell_alloc));

   columns=col_ruler::construct(*c.columns);
}

void Table::push_back_new_facet(facet* f)
{
   f->id=new_id();
   push_back_facet(f);
   ++_size;
}

void Table::push_back_facet(facet* f)
{
   f->list_ptrs.next=end_facet();
   f->list_ptrs.prev=facets.prev;
   facets.prev->list_ptrs.next=f;
   facets.prev=f;
}

void Table::erase_facet(const facet& f)
{
   facet* fp=const_cast<facet*>(&f);
   fp->unlink_cells(cell_alloc);
   facet* prev=fp->list_ptrs.prev;
   facet* next=fp->list_ptrs.next;
   prev->list_ptrs.next=next;
   next->list_ptrs.prev=prev;
   facet_alloc.reclaim(fp);
   --_size;
}

void Table::clear()
{
   facet_alloc.clear();
   cell_alloc.clear();
   _size=0;
   facets.next=facets.prev=end_facet();
   columns=col_ruler::resize(columns, 0);
}

void Table::clear_facets()
{
   facet_alloc.clear();
   cell_alloc.clear();
   _size=0;
   facets.next=facets.prev=end_facet();
   next_id=0;
   for (Entire<col_ruler>::iterator col_it=entire(*columns); !col_it.at_end();  ++col_it) {
      col_it->first_col=NULL;
      col_it->first_lex=NULL;
   }
}

#if POLYMAKE_DEBUG

static
PlainPrinter<>& _show_facet(PlainPrinter<>& os, const facet& f, bool& shown)
{
   if (!shown) {
      shown=true;
      os << "Table::check - facet " << reinterpret_cast<const Facet&>(f) << endl;
   }
   return os;
}

bool Table::sanity_check() const
{
   bool OK=true;
   size_t n_f=0;
   std::vector<int> hist(n_vertices());
   for (const_iterator f=begin(), fe=end();  f != fe;  ++f, ++n_f) {
      int n_e=0;
      int prev_i=-1;
      bool shown=false, lex_prev_seen=false;
      for (Entire<facet>::const_iterator e=entire(*f); !e.at_end(); ++e, ++n_e) {
         int i=e.index();
         if (i<=prev_i)
            _show_facet(cerr, *f, shown) << "order violation" << endl;
         prev_i=i;
         ++hist[i];

         if (e->col.prev == NULL)
            _show_facet(cerr, *f, shown) << "col.prev[" << i << "]=NULL" << endl;
         else if (e->col.prev->col.next != e.operator->())
            _show_facet(cerr, *f, shown) << "col.prev[" << i << "] mismatch" << endl;
         else if (e->col.next != NULL && e->col.next->col.prev != e.operator->())
            _show_facet(cerr, *f, shown) << "col.next[" << i << "] mismatch" << endl;

         if (e->lex.prev != NULL && e->lex.prev->lex.next != e.operator->())
            _show_facet(cerr, *f, shown) << "lex.prev[" << i << "] mismatch" << endl;
         else if (e->lex.next && e->lex.next->lex.prev != e.operator->())
            _show_facet(cerr, *f, shown) << "lex.next[" << i << "] mismatch" << endl;

         if (e->lex.prev != NULL) {
            if (lex_prev_seen)
               _show_facet(cerr, *f, shown) << "lex.prev[" << i << "] multiple" << endl;
            else
               lex_prev_seen=true;
         }
         if (e->lex.next != NULL) {
            if (!lex_prev_seen) {
               _show_facet(cerr, *f, shown) << "lex.next[" << i << "] before lex.prev" << endl;
            } else {
               cell_iterator<&cell::lex> ci(e->lex.next);
               if (reinterpret_cast<const Facet&>(*f) >= reinterpret_cast<const Facet&>(*ci.get_facet()))
                  _show_facet(cerr, *f, shown) << "lexical order violation with "
                                               << reinterpret_cast<const Facet&>(*ci.get_facet()) << endl;
               else if (incl(reinterpret_cast<const Facet&>(*f), reinterpret_cast<const Facet&>(*ci.get_facet())) != 2)
                  _show_facet(cerr, *f, shown) << "inclusion independence violated with "
                                               << reinterpret_cast<const Facet&>(*ci.get_facet()) << endl;
            }
         }
      }
      if (n_e != f->size())
         _show_facet(cerr, *f, shown) << "size mismatch: " << n_e << "!=" << f->size() << endl;

      if (shown) OK=false;
   }

   if (n_f != _size) {
      OK=false;
      cerr << "Table::check - total size violation: " << n_f << "!=" << _size << endl;
   }
   for (Entire<col_ruler>::const_iterator ci=entire(*columns); !ci.at_end(); ++ci) {
      int n_e=0;
      for (Entire<vertex_list>::const_iterator e=entire(*ci); !e.at_end(); ++e, ++n_e) ;
      if (n_e != hist[ci->vertex]) {
         cerr << "Table::check - column counter[" << ci->vertex << "] mismatch: " << n_e << "!=" << hist[ci->vertex] << endl;
         OK=false;
      }
      for (cell_iterator<&cell::lex> e(ci->first_lex); !e.at_end(); ++e) {
         if (e->facet.prev != e.get_facet()->head_cell()) {
            cerr << "Table::check - first_lex[" << ci->vertex << "] linked with "
                 << reinterpret_cast<const Facet&>(*e.get_facet()) << endl;
            OK=false;
         }
      }
   }
   return OK;
}
#endif

} // end namespace pm::fl_internal

#if POLYMAKE_DEBUG
bool FacetList::sanity_check() const { return table->sanity_check(); }
void FacetList::dump() const { cerr << *this << std::flush; }
#endif

}

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
