#include "cdabs.h"

// This File contains routines to check whether 2 adjacencies are similar

void Adjacency_Nlist::similar(Adjacency* adj,Adjacency_Nlist* anl)
{
  Adjacency_list_iterator next_arg(*this);
  Adjacency_ each_arg;
  while (each_arg = next_arg())
    if (each_arg->similar(adj,anl,this))
      return;  /* similar found */
}

int Adjacency::similar(Adjacency* adj,Adjacency_Nlist* anl1,Adjacency_Nlist* anl2)
{
  if (strcmp(this->ns_type(),adj->ns_type()))
    return (FALSE); /* Different types - can't be similar */
  if (!(source->same_name(adj->get_source())))
    return(FALSE); /* different names - can't be similar */
  extern Adjacency_Nlist* new_anl;
  Neighbors* new_ns;
  if (new_ns = ns->similar(adj->get_ns(),anl1,anl2))
    { Adjacency* new_adj = new Adjacency();
      new_adj->set_ns(new_ns);
      new_adj->set_source(source);
      new_anl->append(new_adj); /* appending a new adjacency */
      return (TRUE);
    }
  else return (FALSE);
}

Term* Adjacency::sim_alt(Adjacency* adj,Adjacency_Nlist* anl1,Adjacency_Nlist* anl2)
// Function to find out the common children between 2 Alternate classes
{
  Vertex* new_vertex = ((Vertex*)adj->get_source()->g_copy())->cat((Vertex*)source->g_copy());
  Normal* ret_term = new Normal();
  ret_term->set_vertex(new_vertex);
  extern Adjacency_Nlist* new_anl;
  if (new_anl->get_adj(new_vertex)) /* already found out */
    return (ret_term);
  Neighbors* new_ns;
  if (new_ns = ns->similar(adj->get_ns(),anl1,anl2))
    { Adjacency* new_adj = new Adjacency();
      new_adj->set_ns(new_ns);
      new_adj->set_source(new_vertex);
      new_anl->append(new_adj);
      return (ret_term);
    }
  else return ((Term*)NULL);
}


int Vertex::same_name(Vertex* vertex)
// Checks whether the 2 vertices have the same name
{
  return(vertex_name->g_equal(vertex->get_vertex_name()));
}

Neighbors* Neighbors::similar( Neighbors*, Adjacency_Nlist*, Adjacency_Nlist* )
{
  return ((Neighbors*)NULL);
}

Neighbors* Repetit_n::similar(Neighbors* n,Adjacency_Nlist* anl1,Adjacency_Nlist* anl2)
{
  return (sandwiched->similar(n, anl1, anl2));
}

Neighbors* Neighbors_wc::similar(Neighbors*,Adjacency_Nlist*,Adjacency_Nlist*)
{
  return ((Neighbors*)NULL);
}

Neighbors* Alternat_ns::similar(Neighbors* n,Adjacency_Nlist* anl1,Adjacency_Nlist* anl2)
{
  Term_Bar_list* ret_tbl;
  if (ret_tbl = this->get_alternat_ns()->similar(((Alternat_ns*)n)->get_alternat_ns(),anl1,anl2))
    { Alternat_ns* ret_alns = new Alternat_ns();
      ret_alns->set_alternat_ns(ret_tbl);
      ret_alns->set_construct_ns(new Any_vertex_List());
      return (ret_alns);}
  else 
    return ((Neighbors*)NULL);
}

Neighbors* Construct_ns::similar(Neighbors* n,Adjacency_Nlist* anl1,Adjacency_Nlist* anl2)
{
  Construct_ns* ret_cns = new Construct_ns();
  Any_vertex_List* ret_avl;
  if (ret_avl = this->get_construct_ns()->similar(((Construct_ns*)n)->get_construct_ns(),anl1,anl2))
    { ret_cns->set_construct_ns(ret_avl);
      return (ret_cns);}
  else return ((Neighbors*)NULL);
}

Any_vertex_List* Any_vertex_List::similar( Any_vertex_List* avl, 
					   Adjacency_Nlist* anl1,
					   Adjacency_Nlist* anl2)
{
  Any_vertex_List* ret_avl = new Any_vertex_List();
  if ((this->list_length() == 0) && (avl->list_length() == 0))
    return (ret_avl);
  Any_vertex_list_iterator next_arg(*this);
  Any_vertex_ each_arg;
  Any_vertex_ each_arg1;
  while (each_arg = next_arg())
    if (each_arg1 = each_arg->sim_in_list(avl,anl1,anl2))
      ret_avl->append(each_arg1);
  return (ret_avl);
}

Any_vertex* Any_vertex::sim_in_list( Any_vertex_List*, 
				     Adjacency_Nlist*, 
				     Adjacency_Nlist* )
/* Checks to see if the given vertex has a similar one in the list */
{
  return NULL;
}
 
Any_vertex* Regular_syntax::sim_in_list( Any_vertex_List*, 
					 Adjacency_Nlist*,
					 Adjacency_Nlist*)
{
  return ((Any_vertex*)NULL); /* Not considering these */
}

Any_vertex* Inherit_term::sim_in_list( Any_vertex_List*, 
				       Adjacency_Nlist*,
				       Adjacency_Nlist*)
{
  return ((Any_vertex*)NULL); /* Not considering these */
}

Any_vertex* Optional_term::sim_in_list( Any_vertex_List* avl, 
					Adjacency_Nlist* anl1,
					Adjacency_Nlist* anl2)
{
  Opt_labeled_term* opt_term = this->get_inner();
  char* type1 = (char *)opt_term->get_type();
  Any_vertex_list_iterator next_arg(*avl);
  Any_vertex_ each_arg;
  Any_vertex_ each_arg1;
  while (each_arg = next_arg())
    {char* type2 = (char *)each_arg->get_type();
     if (each_arg->is_optional()) 
       // both are optional - return optional
       { Opt_labeled_term* opt_term1 = each_arg->get_inner();
	 if (!(strcmp(type1,opt_term1->get_type())))
	   if (each_arg1 = opt_term->similar(opt_term1,anl1,anl2))
	     { Optional_term* ret_opt = new Optional_term();
	       Opt_labeled_term_Sandwich* ret_sand = new Opt_labeled_term_Sandwich();
	       ret_sand->set_first(new Syntax_vertex_List());
	       ret_sand->set_second(new Syntax_vertex_List());
	       ret_sand->set_inner((Opt_labeled_term*)each_arg1);
	       ret_opt->set_opt(ret_sand);
	       return (ret_opt);}
       }
     if (!(strcmp(type1,type2)))
       // One is optional - return if this is similar to the required one
       if (each_arg1 = opt_term->similar(each_arg,anl1,anl2))
	 return (each_arg1);
   }
  return ((Any_vertex*)NULL);
}



Any_vertex* Opt_labeled_term::sim_in_list( Any_vertex_List* avl, 
					   Adjacency_Nlist* anl1,
					   Adjacency_Nlist* anl2)
{
  Any_vertex_list_iterator next_arg(*avl);
  Any_vertex_ each_arg;
  Any_vertex_ each_arg1;
  char* type1 = (char*)this->get_type();
  while (each_arg = next_arg())
    {char* type2 = (char*)each_arg->get_type();
     if (!(strcmp(type1,type2))) /* must be of same type to be similar */
       if (each_arg1 = this->similar(each_arg,anl1,anl2))
	 return (each_arg1);
     if (each_arg->is_optional())
       { Opt_labeled_term* opt_term = each_arg->get_inner();
	 if (!(strcmp(type1,opt_term->get_type())))
           if (each_arg1 = this->similar(opt_term,anl1,anl2))
	     return (each_arg1);}
   }
  return ((Any_vertex*)NULL);
}

Any_vertex* Inherit_term::similar( Any_vertex*, 
				   Adjacency_Nlist*,
				   Adjacency_Nlist* )
{
  return ((Any_vertex*)NULL);
}

Any_vertex* Regular_syntax::similar( Any_vertex*, 
				     Adjacency_Nlist*,
				     Adjacency_Nlist*)
{
  return ((Any_vertex*)NULL);
}

Any_vertex* Opt_labeled_term::similar( Any_vertex*, 
				       Adjacency_Nlist*,
				       Adjacency_Nlist* )
{
  return ((Any_vertex*)NULL);
}

Any_vertex* Labeled::similar( Any_vertex* each_arg, 
			      Adjacency_Nlist* anl1,
			      Adjacency_Nlist* anl2 )
{
  if (this->get_label_name()->g_equal(((Labeled*)each_arg)->get_label_name()))
    /*labels must match */ 
    {Labeled* ret_lab = new Labeled();
     ret_lab->set_label_name(((Labeled*)this)->get_label_name());
     Term* ret_term;
     if (ret_term = this->get_vertex()->similar(((Opt_labeled_term*)each_arg)->get_vertex(),anl1,anl2))
       {ret_lab->set_vertex(ret_term);
	return (ret_lab);}
     return ((Any_vertex*)NULL);}
  return ((Any_vertex*)NULL);
}

Any_vertex* Regular::similar( Any_vertex* each_arg, 
			      Adjacency_Nlist* anl1,
			      Adjacency_Nlist* anl2)
{
  if (!(this->get_vertex()->same_name(((Regular*)each_arg)->get_vertex())))
    // names must match - since the name is considered as a label
    return ((Any_vertex*)NULL);
  Regular* ret_reg = new Regular();
  Term* ret_term;
  if (ret_term = this->get_vertex()->similar(((Opt_labeled_term*)each_arg)->get_vertex(),anl1,anl2))
    {ret_reg->set_vertex(ret_term);
     return (ret_reg);}
  return ((Any_vertex*)NULL);
}

int Term::same_name(Term* term)
{
  return (vertex->same_name(term->get_vertex()));
}


Term_Bar_list* Term_Bar_list::similar( Term_Bar_list* tbl, 
				       Adjacency_Nlist* anl1,
				       Adjacency_Nlist* anl2)
{
  Term_Bar_list* ret_tbl = new Term_Bar_list();
  Term_list_iterator next_arg(*this);
  Term_ each_arg;
  while (each_arg = next_arg())
    ret_tbl = each_arg->sim_in_list(tbl,anl1,anl2,ret_tbl);
  if (ret_tbl->list_length() == 0)
    return ((Term_Bar_list*) NULL);
  return (ret_tbl);
}

Term_Bar_list* Term::sim_in_list( Term_Bar_list* tbl, 
				  Adjacency_Nlist* anl1,
				  Adjacency_Nlist* anl2,
				  Term_Bar_list* ret_tbl)
/* Checks to see if the given term has a similar one in the list */
{
  Term_list_iterator next_arg(*tbl);
  Term_ each_arg;
  while (each_arg = next_arg())
    {Term* ret_term; 
     if (ret_term = this->similar(each_arg,anl1,anl2))
       ret_tbl->append(ret_term);}
  return (ret_tbl);
}


Term* Term::similar(Term* term, Adjacency_Nlist* anl1,Adjacency_Nlist* anl2)
{
  Adjacency* adj1;
  if (!(adj1 = anl1->get_adj(term->get_vertex())))
    /* Since not in the adjacency list must be a terminal class */
    if (this->same_name(term)) 
      return (this); /* same terminal class */
    else return ((Term*)NULL); /* different terminal classes */
  Adjacency* adj2; 
  if (!(adj2 = anl2->get_adj(this->get_vertex())))
    return ((Term*)NULL); /* this is a terminal class - not the other*/
  char* type1 = adj1->ns_type();
  char* type2 = adj2->ns_type();
  if (strcmp(type1,type2)) /* different classes */
    { int gen = adj2->generalize(adj1,anl1,anl2);
      if (gen == SUP) /* this class generalizes the other */
	return (term);
      else if (gen == SUB) /* this class specializes the other */
	return (this);}
  else if (!(adj1->is_alns()))
    { if (adj1->get_source()->same_name(adj2->get_source()))
	// same construction or repetition class
	return (this);}
  else { Normal* ret_normal = new Normal();
	 // check to see the commonality between the 2 alternation classes
	 if (ret_normal = ((Normal*)adj2->sim_alt(adj1,anl1,anl2)))
	   return (ret_normal);}
  return ((Term*)NULL);
}

Neighbors* Kernel_Sandwich::similar( Neighbors* n,
				     Adjacency_Nlist* anl1,
				     Adjacency_Nlist* anl2)
{
  return (inner->similar(n,anl1,anl2));
}

Neighbors* Kernel::similar( Neighbors* n,
			    Adjacency_Nlist* anl1,
			    Adjacency_Nlist* anl2)
{
  Term* this_term = repeated->get_inner(); /* get the repeated term */
  Term* n_term = n->get_rep(); /* get the repeated term */
  Term* ret_term;
  if (ret_term = this_term->similar(n_term,anl1,anl2))
    // the terms are similar - return a repetition class
    { Kernel* ret_kernel = new Kernel();
      Term_Sandwich* term_sand = new Term_Sandwich();
      term_sand->set_first(new Syntax_vertex_List());
      term_sand->set_second(new Syntax_vertex_List());
      term_sand->set_inner(ret_term);
      ret_kernel->set_repeated(term_sand);
      if (nonempty && n->ne())
	// both are non-empty - return a non-empty repetition class
	ret_kernel->set_nonempty(ret_term);
      Kernel_Sandwich* kernel_sand = new Kernel_Sandwich();
      kernel_sand->set_first(new Syntax_vertex_List());
      kernel_sand->set_second(new Syntax_vertex_List());
      kernel_sand->set_inner(ret_kernel);
      Repetit_n* ret_n = new Repetit_n();
      ret_n->set_sandwiched(kernel_sand);
      return(ret_n);}
  return ((Neighbors*)NULL);
}

