/* @(#)operators.c	1.13	2/10/93 */
/*******************************************************************************

This code was written and designed by 
Andrew Gene HALL of I.N. Services Pty. Ltd., Scarborough WA, Australia,
for the 
University of Western Australia, Crawley WA, Australia.

*******************************************************************************/

#include "operators.h"
#include <string.h>
#include <ctype.h>
#include "marker.h"

/* external functions */
extern char *strdup( char *) ; /* CHANGE 9/3 */
extern double	strtod();
extern char	*tsearch();
extern char	*tfind();

extern Boolean  strip_stream; /* CHANGE 17/6 */

/* apply_fn_over_LVS
 * Select that data from a List Value Sequence,
 * that is true for the given operator and a constant Data Item.
 * Update the tree of selected data.
 */
void		apply_fn_over_LVS(
List_Value_Seq	*data,
Data_Item	*item,
Boolean		(*operator)( Data_Item *, Data_Item *),
Data_Item	**R_Data_Item,
char		**R_frame_codes,
Data_Item	**C_Data_Item,
char		**C_frame_codes,
Boolean		*R_found,
Boolean		*C_found)
{
   for(; data; data = data->next )
      switch( data->tag ) {
      case DATA_ITEM:
           if( operator( data->element.data_item, item ) ) {
              /* keep this data */
              *R_found = TRUE;
              Mark_Data_Item( data->element.data_item, R_Data_Item );
              if( data->element.data_item->tag == FRAME_CODE_DI )
                 /* save the reference for later */
                 (void)tsearch(data->element.data_item->value,R_frame_codes,strcmp);
           }
           else {
              /* add this data to the complement */
              *C_found = TRUE;
              Mark_Data_Item( data->element.data_item, C_Data_Item );
              if( data->element.data_item->tag == FRAME_CODE_DI )
                 /* save the reference for later */
                 (void)tsearch(data->element.data_item->value,C_frame_codes,strcmp);
           }
           break;

      case SUB_LOOP:
           apply_fn_over_LVS( data->element.sub_loop, item, operator,
                              R_Data_Item, R_frame_codes,
                              C_Data_Item, C_frame_codes,
                              R_found, C_found);
         break;

      default:
         ERROR_MSG("apply_fn_over_LVS(): Unknown domain in LVS.");
      }
} /* apply_fn_over_LVS */

/* apply_fn_over_DS
 * Select that data from the given Data Sequence,
 * that is true for the given operator and constant Data Item.
 * Return the selected data,
 * Update the given data suchthat it points to that data that is not selected.
 */
Data_Seq	*apply_fn_over_DS(
Data_Seq	**orig_data,
Data_Item	*item,
Boolean		(*operator)( Data_Item *, Data_Item *))
{
   Data_Item	*R_Data_Item = NULL;
   char *R_save_refs = NULL;
   char *R_frame_codes = NULL;
   char *R_known_save_blocks = NULL;
   Data_Item	*C_Data_Item = NULL;
   char *C_save_refs = NULL;
   char *C_frame_codes = NULL;
   char *C_known_save_blocks = NULL;
   Data_Seq	*rtn_data = NULL; /* the data satisfying the operation */
   Data_Seq	*data = *orig_data;

   for( ; data; data = data->next) 
      switch( data->tag ) {
      case DATA_ITEM:
         if( operator( data->data.data_item, item ) ) {
            /* keep this data */
            Mark_Data_Item( data->data.data_item, &R_Data_Item );
            if( data->data.data_item->tag == FRAME_CODE_DI )
               /* save the reference for later */
               (void)tsearch(data->data.data_item->value,&R_frame_codes,strcmp);
         }
         else {
            /* add this data to the complement */
            Mark_Data_Item( data->data.data_item, &C_Data_Item );
            if( data->data.data_item->tag == FRAME_CODE_DI )
               /* save the reference for later */
               (void)tsearch(data->data.data_item->value,&C_frame_codes,strcmp);
         }
         break;

      case LOOP: {
           Boolean	found; /* not used */

           apply_fn_over_LVS( data->data.loop.values, item, operator,
                              &R_Data_Item, &R_frame_codes,
                              &C_Data_Item, &C_frame_codes,
                              &found, &found);
           } break;

      case SAVE_BLOCK: {
           Data_Seq	*d;
           char		*tmp_string;
           Boolean	Result_found = FALSE;
           Boolean	Complement_found = FALSE;

           /* apply function over the Save_Block */
           for( d = data->data.save_block; d; d = d->next )
              switch( d->tag ) {
              case DATA_ITEM:
                   if( operator( d->data.data_item, item ) ) {
                      /* keep this data */
                      Mark_Data_Item( d->data.data_item, &R_Data_Item );
                      if( d->data.data_item->tag == FRAME_CODE_DI )
                        /* save the reference for later */
                        (void)tsearch(d->data.data_item->value,&R_frame_codes,strcmp);
                      Result_found = TRUE;
                   }
                   else {
                      /* add this data to the complement */
                      Mark_Data_Item( d->data.data_item, &C_Data_Item );
                      if( d->data.data_item->tag == FRAME_CODE_DI )
                         /* save the reference for later */
                         (void)tsearch(d->data.data_item->value,&C_frame_codes,strcmp);
                      Complement_found = TRUE;
                   }
                   break;
              case LOOP:
                   apply_fn_over_LVS( d->data.loop.values, item, operator,
                                      &R_Data_Item, &R_frame_codes,
                                      &C_Data_Item, &C_frame_codes,
                                      &Result_found, &Complement_found);
                   break;
              default:
                   ERROR_MSG("apply_fn_over_DS: unknown domain in sequence.");
              }
              if( Result_found) {
                  CONVERT_TO_FRAME_CODE( tmp_string, data->name );
                  (void)tsearch( tmp_string, (char **)&R_save_refs, strcmp);
              }
              if( Complement_found) {
                  CONVERT_TO_FRAME_CODE( tmp_string, data->name );
                  (void)tsearch( tmp_string, (char **)&C_save_refs, strcmp);
              }
           } break;

      default:
         ERROR_MSG("apply_fn_over_DS(): Unknown Domain in Data Sequence");
      }

   /* Cross reference so that all Frame Codes are resolved */
   refs_by_frame_codes(*orig_data,&R_Data_Item,&R_frame_codes, &R_save_refs,
                       &R_known_save_blocks);
   refs_by_frame_codes(*orig_data,&C_Data_Item,&C_frame_codes, &C_save_refs,
                       &C_known_save_blocks);

   /* For all Save Blocks found,
      locate the references to them */
   refs_to_SB( *orig_data, &R_save_refs, &R_Data_Item);
   refs_to_SB( *orig_data, &C_save_refs, &C_Data_Item);

   /* build the necessary data sequences */
   rtn_data = get_Marked_DS( *orig_data, &R_Data_Item, &R_known_save_blocks );
   /* update the complement */
   *orig_data = get_Marked_DS( *orig_data, &C_Data_Item, &C_known_save_blocks );
   return( rtn_data );
} /* apply_fn_over_DS */

Block_Seq	*apply_fn_over_BS(
Block_Seq	**orig_blocks,
Data_Item	*data_item,
Boolean		(*operator)( Data_Item *, Data_Item *))
{
   Block_Seq	*rtn_blocks = NULL; /* the data satisfying the operation */
   Block_Seq	**next_blocks = &rtn_blocks;
   Block_Seq	*comp_blocks = NULL; /* the data complement to the operation */
   Block_Seq	**next_comp_blocks = &comp_blocks;
   Block_Seq	*save_global_block = NULL;
   Block_Seq	*save_global_comp_block = NULL;
   Block_Seq	*blocks = *orig_blocks;
   Data_Seq	*tmp, *complement;
   Global_Seq	*globals, *complement_globals;

   while( blocks ) {
         register Domain	tag;

         tag = blocks->tag;
         switch( tag ) {
         case DATA_BLOCK:
              complement = blocks->block.data_block.actual_data;
              break;
         case GLOBAL_BLOCK:
              complement = blocks->block.global_block;
              break;
         default: ERROR_MSG("apply_fn_over_BS: unknown block in Block_Seq.");
         }
         if( (tmp = apply_fn_over_DS( &complement, data_item, operator)) ) {
            if( complement ) {
               /* build a new node for the block */
               Malloc( Block_Seq, *next_blocks );
               (*next_blocks)->name = blocks->name;
               switch( tag ) {
               case DATA_BLOCK:
                    (*next_blocks)->tag = DATA_BLOCK;
                    (*next_blocks)->block.data_block.actual_data = tmp;
                    (*next_blocks)->block.data_block.global_data = globals;
                    break;
               case GLOBAL_BLOCK:
                    (*next_blocks)->tag = GLOBAL_BLOCK;
                    (*next_blocks)->block.global_block = tmp;
                    break;
               default:
                    ERROR_MSG("apply_fn_over_BS: unknown block in Block_Seq.");
               }
               (*next_blocks)->next = NULL;
               save_global_block = *next_blocks;
               next_blocks = &((*next_blocks)->next);
               /* replace the complement in blocks */

               (*next_comp_blocks) = blocks;
               switch( tag ) {
               case DATA_BLOCK:
                    blocks->block.data_block.actual_data = complement;
                    blocks->block.data_block.global_data = complement_globals;
                    break;
               case GLOBAL_BLOCK:
                    blocks->block.global_block = complement;
                    break;
               default:
                    ERROR_MSG("apply_fn_over_BS: unknown block in Block_Seq.");
               }
               next_comp_blocks = &(blocks->next);
               save_global_comp_block = blocks;
               blocks = blocks->next;
               (*next_comp_blocks) = NULL;
            }
            else {
               /* use the existing node */
               (*next_blocks) = blocks;
               switch( tag ) {
               case DATA_BLOCK:
                    (*next_blocks)->block.data_block.actual_data = tmp;
                    (*next_blocks)->block.data_block.global_data = globals;
                    break;
               case GLOBAL_BLOCK:
                    (*next_blocks)->block.global_block = tmp;
                    break;
               default:
                    ERROR_MSG("apply_fn_over_BS: unknown block in Block_Seq.");
               }
               next_blocks = &(blocks->next);
               save_global_block = blocks;
               blocks = blocks->next;
               (*next_blocks) = NULL;
            }
         }
         else
            if( complement ) {
               (*next_comp_blocks) = blocks;
               switch( tag ) {
               case DATA_BLOCK:
                    blocks->block.data_block.actual_data = complement;
                    blocks->block.data_block.global_data = complement_globals;
                    break;
               case GLOBAL_BLOCK:
                    blocks->block.global_block = complement;
                    break;
               default:
                    ERROR_MSG("apply_fn_over_BS: unknown block in Block_Seq.");
               }
               next_comp_blocks = &(blocks->next);
               blocks = blocks->next;
               save_global_comp_block = blocks;
               (*next_comp_blocks) = NULL;
            }
            else
               blocks = blocks->next;

         if( tag == GLOBAL_BLOCK ) {
            Global_Seq	*tmp_globals;

            if( save_global_block ) {
               Malloc( Global_Seq, tmp_globals );
               tmp_globals->global_entry = save_global_block;
               tmp_globals->next = globals;
               globals = tmp_globals;
            }
            if( save_global_comp_block ) {
               Malloc( Global_Seq, tmp_globals );
               tmp_globals->global_entry = save_global_comp_block;
               tmp_globals->next = complement_globals;
               complement_globals = tmp_globals;
            }
         }
         save_global_block = NULL;
         save_global_comp_block = NULL;
      }

   /* return */
   *orig_blocks = comp_blocks;
   return( rtn_blocks );
} /* apply_fn_over_BS */


/*****************/
/* Set functions */
/*****************/

/* Complement_LVS
 * Find the Complement Data Items from an LVS
 */
void		Complement_LVS(
List_Value_Seq  *list,
Data_Item	**Intersection_tree,
Data_Item	**Complement_tree,
char		**C_frame_codes,
Boolean		*Complement_found)
{
   for( ; list; list = list->next)
      switch( list->tag ) {
      case DATA_ITEM:
           if( ! tfind((char *)list->element.data_item,
                    (char **)Intersection_tree,compare_2_DI)) {
              *Complement_found = TRUE;
              /* add this data to the complement */
              Mark_Data_Item( list->element.data_item, Complement_tree);
              if( list->element.data_item->tag == FRAME_CODE_DI )
                 (void)tsearch(list->element.data_item->value, C_frame_codes, strcmp);
           }
           break;

      case SUB_LOOP: 
           Complement_LVS( list->element.sub_loop,
                          Intersection_tree,
                          Complement_tree, C_frame_codes, Complement_found);
           break;

      default:
           ERROR_MSG("Complement_LVS(): Unknown domain in LVS.");
      }

} /* Complement_LVS() */

/* intersecting_LVS
 * Find the intersecting Data Items from an LVS
 */
void		Intersecting_LVS(
List_Value_Seq  *list,
Data_Item	**initial_tree,
Data_Item	**Intersection_tree,
char		**I_frame_codes,
Boolean		*Intersection_found,
Data_Item	**Complement_tree,
char		**C_frame_codes,
Boolean		*Complement_found)
{
   for( ; list; list = list->next)
      switch( list->tag ) {
      case DATA_ITEM:
           if(tfind((char *)list->element.data_item,
                    (char **)initial_tree,compare_2_DI)) {
              *Intersection_found = TRUE;
              Mark_Data_Item( list->element.data_item, Intersection_tree);
              if( list->element.data_item->tag == FRAME_CODE_DI )
                 (void)tsearch(list->element.data_item->value, I_frame_codes, strcmp);
           } else {
              *Complement_found = TRUE;
              /* add this data to the complement */
              Mark_Data_Item( list->element.data_item, Complement_tree);
              if( list->element.data_item->tag == FRAME_CODE_DI )
                 (void)tsearch(list->element.data_item->value, C_frame_codes, strcmp);
           }
           break;

      case SUB_LOOP: 
           Intersecting_LVS( list->element.sub_loop, initial_tree,
                          Intersection_tree, I_frame_codes, Intersection_found,
                          Complement_tree, C_frame_codes, Complement_found);
           break;

      default:
           ERROR_MSG("intersecting_LVS(): Unknown domain in LVS.");
      }

} /* intersecting_LVS() */

/* remove_redundant_LVS
 * Scan along the given list of values,
 * removing those already present in the given tree
 */
List_Value_Seq	*remove_redundant_LVS(
List_Value_Seq	*list,
Data_Item	**tree)
{
   List_Value_Seq	*values = list;
   List_Value_Seq	**prev_value = &values;

   for( ; list; list = list->next)
      switch( list->tag ) {
      case DATA_ITEM:
           /* check if we already have it */
           if(tfind((char *)list->element.data_item,(char **)tree,compare_2_DI))
              /* delete this node */
              *prev_value = list->next;
           else
              prev_value = &(list->next);
           break;

      case SUB_LOOP: {
           List_Value_Seq	*l;
 
           l = remove_redundant_LVS( list->element.sub_loop, tree);
           if( l ) {
              list->element.sub_loop = l;
              prev_value = &(list->next);
           } else
              /* delete this node */
              *prev_value = list->next;
           } break;

      default:
           ERROR_MSG("remove_redundant_LVS(): Unknown domain in LVS.");
      }
   return( values );
} /* remove_redundant_LVS */

/* union_DS
 * Given two sequencences of data,
 * Unite the two, placing the second after the first.
 * No complement is returned.
 */
Data_Seq	*union_DS(
Data_Seq        *data1,
Data_Seq        *data2)
{
  register Data_Seq      *pos;
  Data_Seq      *second_data = data2;

  /* scan down the first sequence, eliminating redundant data */
  for( pos = data1; pos; pos = pos->next) {
     Data_Seq	**prev_data = &second_data;
     Data_Seq	*d = second_data;

     while( d )
        if( ! strcmp( pos->name, d->name) ) {
           switch( pos->tag ) {
           case DATA_ITEM:
                break;

           case LOOP: {
                Data_Item	*root = NULL;
                char		*dummy_char = NULL;
                List_Value_Seq	*l;

                Mark_LVS( pos->data.loop.values, &root, &dummy_char );
                l = remove_redundant_LVS( d->data.loop.values, &root);
                /* place trimmed LVS on the end */
                if( pos->data.loop.values ) {
                   register List_Value_Seq	*l2 = pos->data.loop.values;

                   while( l2->next ) l2 = l2->next;
                   l2->next = l;
                } else
                   pos->data.loop.values = l;
                } break;

           case SAVE_BLOCK:
                pos->data.save_block = union_DS( pos->data.save_block, 
                                                 d->data.save_block);
                break;

           default:
                ERROR_MSG("union_DS(): unknown domain in sequence.");
           }
           /* remove this data from the second sequence */
           *prev_data = d->next;
           /* terminate the scanning */
           d = NULL; 
        } else {
           prev_data = &(d->next);
           d = d->next;
        }
  }

  /* place whats left of the second sequence on the end */
  if( data1 ) {
     pos = data1; while( pos->next ) pos = pos->next;
     pos->next = second_data;
  } else
     data1 = second_data;
  return( data1 );
} /* union_DS */

/* union_BS 
 * Given two block sequences,
 * Unite the two with the first coming first 
 * and the remainder of the second after.
 * No complement is returned.
 */
Block_Seq       *union_BS(
Block_Seq       *seq1,
Block_Seq       *seq2)
{
   register Block_Seq * last_R_bs = NULL;       /* resultant blocks */
   Block_Seq *          tmp_R_bs = NULL;
   Global_Seq           *R_globals = NULL;
   Block_Seq            *blocks = seq2;

   /* cycle through the contents of seq1 */
   for( ; seq1; seq1 = seq1->next ) {
      Block_Seq *b, **prev_seq2, *end_R;

      /* remember end of result set */
      end_R = last_R_bs;

      /* find the block with the same name in seq2 */
      prev_seq2 = &blocks;
      b = blocks;
      while( b )
         if( ! strcmp( seq1->name, b->name ) ) {
            Data_Seq    *data;

            switch( seq1->tag ) {
            case DATA_BLOCK:
                 data = union_DS( seq1->block.data_block.actual_data,
                                  b->block.data_block.actual_data);
                 /* add on to the end of the result */
                 if( last_R_bs ) {
                    Malloc( Block_Seq, last_R_bs->next);
                    last_R_bs = last_R_bs->next;
                 } else 
                    { Malloc( Block_Seq, last_R_bs ); tmp_R_bs = last_R_bs; }
                 last_R_bs->name = seq1->name;
                 last_R_bs->tag = DATA_BLOCK;
                 last_R_bs->block.data_block.actual_data = data;
                 last_R_bs->block.data_block.global_data = R_globals;
                 last_R_bs->next = NULL;

                 /* remove this block from seq2 */
                 *prev_seq2 = b->next;
                 break;

            case GLOBAL_BLOCK: {
                 Global_Seq     *tmp_globals = NULL;

                 data = union_DS( seq1->block.data_block.actual_data,
                                  b->block.data_block.actual_data);
                 /* add on to the end of the result */
                 if( last_R_bs ) {
                    Malloc( Block_Seq, last_R_bs->next);
                    last_R_bs = last_R_bs->next;
                 } else 
                    { Malloc( Block_Seq, last_R_bs ); tmp_R_bs = last_R_bs; }
                 last_R_bs->name = seq1->name;
                 last_R_bs->tag = GLOBAL_BLOCK;
                 last_R_bs->block.global_block = data;
                 last_R_bs->next = NULL;

                 /* add this to the head of the list of globals */
                 Malloc( Global_Seq, tmp_globals );
                 tmp_globals->global_entry = last_R_bs;
                 tmp_globals->next = R_globals;
                 R_globals = tmp_globals;

                 /* remove this block from seq2 */
                 *prev_seq2 = b->next;
                 } break;

            default:
                 ERROR_MSG("union_BS(): unknown block in Block_Seq.");
            }
            b = NULL;
         } else {
           prev_seq2 = &(b->next);
           b = b->next;
         }

      /* check if anything was actually found */
      if( end_R == last_R_bs ) {
         /* add the sequence on the end of the result */
         if( last_R_bs ) {
            last_R_bs->next = seq1;
            last_R_bs = last_R_bs->next;
         } else
            tmp_R_bs = last_R_bs = seq1;

         /* update globals as necessary */
         switch( seq1->tag ) {
         case DATA_BLOCK:
              last_R_bs->block.data_block.global_data = R_globals;
              break;

         case GLOBAL_BLOCK: {
              Global_Seq     *tmp_globals = NULL;

              /* add this to the head of the list of globals */
              Malloc( Global_Seq, tmp_globals );
              tmp_globals->global_entry = last_R_bs;
              tmp_globals->next = R_globals;
              R_globals = tmp_globals;
              } break;

         default:
              ERROR_MSG("union_BS(): unknown block in Block_Seq.");
         }
      }
   }

   /* append the secondary set on to the end of the result */
   if( last_R_bs ) {
      last_R_bs->next = blocks;
      last_R_bs = last_R_bs->next;
   } else
      tmp_R_bs = last_R_bs = blocks;
   for( ; blocks; blocks = blocks->next )
         /* update globals as necessary */
         switch( blocks->tag ) {
         case DATA_BLOCK:
              blocks->block.data_block.global_data = R_globals;
              break;

         case GLOBAL_BLOCK: {
              Global_Seq     *tmp_globals = NULL;

              /* add this to the head of the list of globals */
              Malloc( Global_Seq, tmp_globals );
              tmp_globals->global_entry = blocks;
              tmp_globals->next = R_globals;
              R_globals = tmp_globals;
              } break;

         default:
              ERROR_MSG("union_BS(): unknown block in Block_Seq.");
         }

   return( tmp_R_bs );
} /* union_BS */

/* intersection_DS
 * Find the Data Items present in both Data Sequences.
 * Returning a data sequence of the intersection and updating the complement.
 */
Data_Seq	*intersection_DS(
Data_Seq	*data1,
Data_Seq	*data2,
Data_Seq	**complement_data)
{
  register Data_Seq      *d;
  Data_Seq	*result = NULL;
  Data_Item    *initial_tree = NULL;
  Data_Item    *Intersection_tree = NULL;
  char *I_save_refs = NULL;
  char *I_frame_codes = NULL;
  char *I_known_save_blocks = NULL;
  Data_Item    *Complement_tree = NULL;
  char *C_save_refs = NULL;
  char *C_frame_codes = NULL;
  char *C_known_save_blocks = NULL;
  char	*dummy_tree = NULL;

  /* foreach Node in Data1, Mark all its Data Items */
  for( d = data1; d; d = d->next)
     switch( d->tag ) {
     case DATA_ITEM:
        Mark_Data_Item( d->data.data_item, &initial_tree);
        break;
     case LOOP:
        Mark_LVS( d->data.loop.values, &initial_tree, &dummy_tree);
        break;
     case SAVE_BLOCK: {
        Data_Seq	*n;

        for( n = d->data.save_block; n; n = n->next )
           switch( n->tag ) {
           case DATA_ITEM:
              Mark_Data_Item( n->data.data_item, &initial_tree);
              break;
           case LOOP:
              Mark_LVS( n->data.loop.values, &initial_tree, &dummy_tree);
              break;
           default:
               ERROR_MSG("intersection_DS(): Unknown Domain in Data Sequence");
           }
        } break;
     default:
         ERROR_MSG("intersection_DS(): Unknown Domain in Data Sequence");
     }

  /* foreach Node in Data2, Add that Data which intersects to the intersect tree
                            otherwise add it to the complement tree */
  for( d = data2; d; d = d->next)
     switch( d->tag ) {
     case DATA_ITEM:
        if( tfind((char *)d->data.data_item, (char **)&initial_tree,
            compare_2_DI) ) {
           Mark_Data_Item( d->data.data_item, &Intersection_tree);
           if( d->data.data_item->tag == FRAME_CODE_DI )
              (void)tsearch(d->data.data_item->value, &I_frame_codes, strcmp);
        } else {
           /* add this data to the complement */
           Mark_Data_Item( d->data.data_item, &Complement_tree);
           if( d->data.data_item->tag == FRAME_CODE_DI )
              (void)tsearch(d->data.data_item->value, &C_frame_codes, strcmp);
        }
        break;
     case LOOP: {
        Boolean	dummy;

        Intersecting_LVS( d->data.loop.values, &initial_tree,
                          &Intersection_tree, &I_frame_codes, &dummy,
                          &Complement_tree, &C_frame_codes, &dummy);
        } break;
     case SAVE_BLOCK: {
        Data_Seq	*n;
        Boolean	Intersection_found = FALSE;
        Boolean	Complement_found = FALSE;
        char	*tmp_string;

        for( n = d->data.save_block; n; n = n->next )
           switch( n->tag ) {
           case DATA_ITEM:
              if( tfind((char *)n->data.data_item, (char **)&initial_tree,
                  compare_2_DI) ) {
                 Mark_Data_Item( n->data.data_item, &Intersection_tree);
                 if( n->data.data_item->tag == FRAME_CODE_DI )
                    (void)tsearch(n->data.data_item->value, &I_frame_codes, strcmp);
                 Intersection_found = TRUE;
              } else {
                 /* add this data to the complement */
                 Mark_Data_Item( n->data.data_item, &Complement_tree);
                 if( d->data.data_item->tag == FRAME_CODE_DI )
                    (void)tsearch(n->data.data_item->value, &C_frame_codes, strcmp);
                 Complement_found = TRUE;
              }
              break;
           case LOOP:
              Intersecting_LVS( n->data.loop.values, &initial_tree,
                                &Intersection_tree, &I_frame_codes, &Intersection_found,
                                &Complement_tree, &C_frame_codes, &Complement_found);
              break;
           default:
               ERROR_MSG("intersection_DS(): Unknown Domain in Data Sequence");
           }

        if( Intersection_found) {
            CONVERT_TO_FRAME_CODE( tmp_string, d->name );
            (void)tsearch( tmp_string, (char **)&I_save_refs, strcmp);
            (void)tsearch( d->name, (char **)&I_known_save_blocks, strcmp);
        }
        if( Complement_found) {
            CONVERT_TO_FRAME_CODE( tmp_string, d->name );
            (void)tsearch( tmp_string, (char **)&C_save_refs, strcmp);
            (void)tsearch( d->name, (char **)&C_known_save_blocks, strcmp);
        }
        } break;
     default:
         ERROR_MSG("intersection_DS(): Unknown Domain in Data Sequence");
     }

  /* foreach Node in Data1, Add that data which is not in the intersection to 
                            the complement */
  for( d = data1; d; d = d->next)
     switch( d->tag ) {
     case DATA_ITEM:
        if( ! tfind((char *)d->data.data_item, (char **)&Intersection_tree,
            compare_2_DI) ) {
           /* add this data to the complement */
           Mark_Data_Item( d->data.data_item, &Complement_tree);
           if( d->data.data_item->tag == FRAME_CODE_DI )
              (void)tsearch(d->data.data_item->value, &C_frame_codes, strcmp);
        }
        break;
     case LOOP: {
        Boolean	dummy;

        Complement_LVS( d->data.loop.values, 
                        &Intersection_tree, 
                        &Complement_tree, &C_frame_codes, &dummy);
        } break;
     case SAVE_BLOCK: {
        Data_Seq	*n;
        Boolean	Complement_found = FALSE;
        char	*tmp_string;

        for( n = d->data.save_block; n; n = n->next )
           switch( n->tag ) {
           case DATA_ITEM:
              if( ! tfind((char *)n->data.data_item,(char **)&Intersection_tree,
                  compare_2_DI) ) {
                 /* add this data to the complement */
                 Mark_Data_Item( n->data.data_item, &Complement_tree);
                 if( d->data.data_item->tag == FRAME_CODE_DI )
                    (void)tsearch(n->data.data_item->value, &C_frame_codes, strcmp);
                 Complement_found = TRUE;
              }
              break;
           case LOOP:
              Complement_LVS( n->data.loop.values, 
                              &Intersection_tree, 
                              &Complement_tree, &C_frame_codes, &Complement_found);
              break;
           default:
               ERROR_MSG("intersection_DS(): Unknown Domain in Data Sequence");
           }

        if( Complement_found) {
            CONVERT_TO_FRAME_CODE( tmp_string, d->name );
            (void)tsearch( tmp_string, (char **)&C_save_refs, strcmp);
            (void)tsearch( d->name, (char **)&C_known_save_blocks, strcmp);
        }
        } break;
     default:
         ERROR_MSG("intersection_DS(): Unknown Domain in Data Sequence");
     }

  /* get the union of data1 and data2 */
  d = union_DS( data1, data2);

   /* Cross reference so that all Frame Codes are resolved */
   refs_by_frame_codes(d, &Intersection_tree, &I_frame_codes, &I_save_refs,
                       &I_known_save_blocks);
   refs_by_frame_codes(d, &Complement_tree, &C_frame_codes, &C_save_refs,
                       &C_known_save_blocks);

   /* For all Save Blocks found,
      locate the references to them */
   refs_to_SB( d, &I_save_refs, &Intersection_tree);
   refs_to_SB( d, &C_save_refs, &Complement_tree);

  /* return the results */
  result = get_Marked_DS( d, &Intersection_tree, &I_known_save_blocks );
  *complement_data = get_Marked_DS(d,&Complement_tree,&C_known_save_blocks);
  return( result );
} /* intersection_DS */

/* intersection_BS
 * Given two Block Sequences,
 * Return those Data Items present in both
 * Changes the structures pointed to by seq1 and seq2.
 */
Block_Seq	*intersection_BS(
Block_Seq	*seq1,
Block_Seq	*seq2,
Block_Seq	**complement)
{
   register Block_Seq * last_R_bs = NULL;	/* resultant blocks */
   Block_Seq *          tmp_R_bs = NULL;
   Global_Seq           *R_globals = NULL;
   register Block_Seq * last_C_bs = NULL;	/* complement blocks */
   Block_Seq *          tmp_C_bs = NULL;
   Global_Seq           *C_globals = NULL;
   Block_Seq		*blocks = seq2;

   /* cycle through the contents of seq1 */
   for( ; seq1; seq1 = seq1->next ) {
      Block_Seq	*b, **prev_seq2, *end_C;

      /* remember end of result set */
      end_C = last_C_bs;

      /* find block with the same name in seq2 */
      prev_seq2 = &blocks;
      b = blocks;
      while( b )
         if( ! strcmp( seq1->name, b->name ) ) {
            Data_Seq	*data, *complement_data;

            switch( seq1->tag ) {
            case DATA_BLOCK:
                 if( (data = intersection_DS(seq1->block.data_block.actual_data,
                                             b->block.data_block.actual_data,
                                             &complement_data)) ) {
                    /* add on to the end of the result */
                    if( last_R_bs ) {
                       Malloc( Block_Seq, last_R_bs->next);
                       last_R_bs = last_R_bs->next;
                    } else 
                       { Malloc( Block_Seq, last_R_bs ); tmp_R_bs = last_R_bs; }
                    last_R_bs->name = seq1->name;
                    last_R_bs->tag = DATA_BLOCK;
                    last_R_bs->block.data_block.actual_data = data;
                    last_R_bs->block.data_block.global_data = R_globals;
                    last_R_bs->next = NULL;
                 }
                 /* add on to the end of the complement set */
                 if( last_C_bs ) {
                    Malloc( Block_Seq, last_C_bs->next);
                    last_C_bs = last_C_bs->next;
                 } else 
                    { Malloc( Block_Seq, last_C_bs ); tmp_C_bs = last_C_bs; }
                 last_C_bs->name = seq1->name;
                 last_C_bs->tag = DATA_BLOCK;
                 last_C_bs->block.data_block.actual_data = complement_data;
                 last_C_bs->block.data_block.global_data = C_globals;
                 last_C_bs->next = NULL;

                 /* remove this block from seq2 */
                 *prev_seq2 = b->next;
                 break;

            case GLOBAL_BLOCK: {
                 Global_Seq	*tmp_globals = NULL;

                 if( (data = intersection_DS(seq1->block.global_block,
                                             b->block.global_block,
                                             &complement_data)) ) {
                    /* add on to the end of the result */
                    if( last_R_bs ) {
                       Malloc( Block_Seq, last_R_bs->next);
                       last_R_bs = last_R_bs->next;
                    } else 
                       { Malloc( Block_Seq, last_R_bs ); tmp_R_bs = last_R_bs; }
                    last_R_bs->name = seq1->name;
                    last_R_bs->tag = GLOBAL_BLOCK;
                    last_R_bs->block.global_block = data;
                    last_R_bs->next = NULL;

                    /* add this to the head of the list of globals */
                    Malloc( Global_Seq, tmp_globals );
                    tmp_globals->global_entry = last_R_bs;
                    tmp_globals->next = R_globals;
                    R_globals = tmp_globals;
                 }
                 /* add on to the end of the complement set */
                 if( last_C_bs ) {
                    Malloc( Block_Seq, last_C_bs->next);
                    last_C_bs = last_C_bs->next;
                 } else 
                    { Malloc( Block_Seq, last_C_bs ); tmp_C_bs = last_C_bs; }
                 last_C_bs->name = seq1->name;
                 last_C_bs->tag = GLOBAL_BLOCK;
                 last_C_bs->block.global_block = complement_data;
                 last_C_bs->next = NULL;

                 /* add this to the head of the list of globals */
                 Malloc( Global_Seq, tmp_globals );
                 tmp_globals->global_entry = last_C_bs;
                 tmp_globals->next = C_globals;
                 C_globals = tmp_globals;

                 /* remove this block from seq2 */
                 *prev_seq2 = b->next;
                 } break;

            default:
                 ERROR_MSG("intersection_BS(): unknown block in Block_Seq.");
            }
            b = NULL;
         } else {
           prev_seq2 = &(b->next);
           b = b->next;
         }

      /* check if anything was found */
      if( end_C == last_C_bs ) {
         /* add the sequence on the end of the complement */
         if( last_C_bs ) {
            last_C_bs->next = seq1;
            last_C_bs = last_C_bs->next;
         } else
            tmp_C_bs = last_C_bs = seq1;

         /* update globals as necessary */
         switch( seq1->tag ) {
         case DATA_BLOCK:
              last_C_bs->block.data_block.global_data = C_globals;
              break;

         case GLOBAL_BLOCK: {
              Global_Seq     *tmp_globals = NULL;

              /* add this to the head of the list of globals */
              Malloc( Global_Seq, tmp_globals );
              tmp_globals->global_entry = last_C_bs;
              tmp_globals->next = C_globals;
              C_globals = tmp_globals;
              } break;

         default:
              ERROR_MSG("intersection_BS(): unknown block in Block_Seq.");
         }
      }
   }

   /* append whats left of the secondary set on to the end of the complement */
   if( last_C_bs ) {
      last_C_bs->next = blocks;
      last_C_bs = last_C_bs->next;
   } else
      tmp_C_bs = last_C_bs = blocks;
   for( ; blocks; blocks = blocks->next ) 
         /* update globals as necessary */
         switch( blocks->tag ) {
         case DATA_BLOCK:
              blocks->block.data_block.global_data = C_globals;
              break;

         case GLOBAL_BLOCK: {
              Global_Seq     *tmp_globals = NULL;

              /* add this to the head of the list of globals */
              Malloc( Global_Seq, tmp_globals );
              tmp_globals->global_entry = blocks;
              tmp_globals->next = C_globals;
              C_globals = tmp_globals;
              } break;

         default:
              ERROR_MSG("intersection_BS(): unknown block in Block_Seq.");
         }

   *complement = tmp_C_bs;
   return( tmp_R_bs );
} /* intersection_BS */


/**********************/
/* Operator Functions */
/**********************/

/* corresponds with the ~= operator */
Boolean	equal_strings(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strcmp( str_strip(item1->value), str_strip(item2->value) ) )
      return( FALSE );
   else
      return( TRUE );
} /* equal_strings */

/* corresponds with the ~!= operator */
Boolean	not_equal_strings(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strcmp( str_strip(item1->value), str_strip(item2->value) ) )
      return( TRUE );
   else
      return( FALSE );
} /* not_equal_strings */

/* corresponds with the ?= operator */
Boolean	sub_string(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strstr( str_strip(item1->value), str_strip(item2->value) ) )
      return( TRUE );
   else
      return( FALSE );
} /* sub_strings */

/* corresponds with the ?!= operator */
Boolean	not_sub_string(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strstr( str_strip(item1->value), str_strip(item2->value) ) )
      return( FALSE );
   else
      return( TRUE );
} /* not_sub_strings */

/* corresponds with the ~< operator */
Boolean	ascii_less(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strcmp( str_strip(item1->value), str_strip(item2->value) ) <0 )
      return( TRUE );
   else
      return( FALSE );
} /* ascii_less */

/* corresponds with the ~<= operator */
Boolean	ascii_less_or_equal(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strcmp( str_strip(item1->value), str_strip(item2->value) ) <= 0 )
      return( TRUE );
   else
      return( FALSE );
} /* ascii_less_or_equal */

/* corresponds with the ~> operator */
Boolean	ascii_greater(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strcmp( str_strip(item1->value), str_strip(item2->value) ) > 0 )
      return( TRUE );
   else
      return( FALSE );
} /* ascii_greater */

/* corresponds with the ~> operator */
Boolean	ascii_greater_or_equal(
Data_Item	*item1,
Data_Item	*item2)
{
   if( strcmp( str_strip(item1->value), str_strip(item2->value) ) >= 0 )
      return( TRUE );
   else
      return( FALSE );
} /* ascii_greater_or_equal */

/* string_to_double
 * Convert the given string to a floating point value,
 * ignoring any esd present.
 * Return error message, if not successfully convereted otherwise NULL.
 * Update rtn_value to the converted value.
 */
char *string_to_double( 
char	*string, 
double	*return_value)
{
   char	*s, *t;

   s = string;
   if( s ) {
      /* do the conversion */
      *return_value = (double)strtod( s, &t);
      if( s == t )
         return(strdup("Could not convert string to double."));
      else {
         /* search for trailing esd */
         /* skip over leading ' ' and '(' */
         while( isspace( *t ) || ( *t == '(' ) ) t++;
         if( *t ) {
            double	esd;

            s = t;
            /* find the esd */
            esd = strtod( s, &t);
            if( s == t )
               return( strdup("Could not convert esd portion of string."));
            else {
               /* skip over trailing ' ' and ')' */
               while( isspace( *t ) || ( *t == ')' ) ) t++;
               if( *t )
                  return(strdup("Trailing garbage in the data."));
               else
                  return( NULL );
            }
         }
         else
            return( NULL );
      }
   }
   else
      return( strdup("Tried to convert a Null string."));
} /* string_to_double */

/* corresponds with the = operator */
Boolean real_equal(
Data_Item       *item1,
Data_Item       *item2)
{
   double x, y;
   char	*errmsg;

   if( (errmsg = string_to_double( item1->value, &x )) ) {
      DEBUG_MSG("real_equal(): Failed to convert item 1 to double.");
      DEBUG_MSG(errmsg);
   }
   else
      if( (errmsg = string_to_double( item2->value, &y )) ) {
         DEBUG_MSG("real_equal(): Failed to convert item 2 to double.");
         DEBUG_MSG(errmsg);
      }
      else
        if( x == y )
           return( TRUE );
        else
           return( FALSE );
   return( FALSE );
} /* real_equal */

/* corresponds with the != operator */
Boolean real_not_equal(
Data_Item       *item1,
Data_Item       *item2)
{
   double x, y;
   char	*errmsg;

   if( (errmsg = string_to_double( item1->value, &x )) ) {
      DEBUG_MSG("Failed to convert item 1 to double.");
      DEBUG_MSG(errmsg);
   }
   else
      if( (errmsg = string_to_double( item2->value, &y )) ) {
         DEBUG_MSG("Failed to convert item 2 to double.");
         DEBUG_MSG(errmsg);
      }
      else
        if( x != y )
           return( TRUE );
        else
           return( FALSE );
   return( FALSE );
} /* real_not_equal */

/* corresponds with the < operator */
Boolean real_less(
Data_Item       *item1,
Data_Item       *item2)
{
   double x, y;
   char	*errmsg;

   if( (errmsg = string_to_double( item1->value, &x )) ) {
      DEBUG_MSG("Failed to convert item 1 to double.");
      DEBUG_MSG(errmsg);
   }
   else
      if( (errmsg = string_to_double( item2->value, &y )) ) {
         DEBUG_MSG("Failed to convert item 2 to double.");
         DEBUG_MSG(errmsg);
      }
      else
        if( x < y )
           return( TRUE );
        else
           return( FALSE );
   return( FALSE );
} /* real_less */

/* corresponds with the <= operator */
Boolean real_less_or_equal(
Data_Item       *item1,
Data_Item       *item2)
{
   double x, y;
   char	*errmsg;

   if( (errmsg = string_to_double( item1->value, &x )) ) {
      DEBUG_MSG("Failed to convert item 1 to double.");
      DEBUG_MSG(errmsg);
   }
   else
      if( (errmsg = string_to_double( item2->value, &y )) ) {
         DEBUG_MSG("Failed to convert item 2 to double.");
         DEBUG_MSG(errmsg);
      }
      else
        if( x <= y )
           return( TRUE );
        else
           return( FALSE );
   return( FALSE );
} /* real_less_or_equal */

/* corresponds with the > operator */
Boolean real_greater(
Data_Item       *item1,
Data_Item       *item2)
{
   double x, y;
   char	*errmsg;

   if( (errmsg = string_to_double( item1->value, &x )) ) {
      DEBUG_MSG("Failed to convert item 1 to double.");
      DEBUG_MSG(errmsg);
   }
   else
      if( (errmsg = string_to_double( item2->value, &y )) ) {
         DEBUG_MSG("Failed to convert item 2 to double.");
         DEBUG_MSG(errmsg);
      }
      else
        if( x > y )
           return( TRUE );
        else
           return( FALSE );
   return( FALSE );
} /* real_greater */

/* corresponds with the >= operator */
Boolean real_greater_or_equal(
Data_Item       *item1,
Data_Item       *item2)
{
   double x, y;
   char	*errmsg;

   if( (errmsg = string_to_double( item1->value, &x )) ) {
      DEBUG_MSG("Failed to convert item 1 to double.");
      DEBUG_MSG(errmsg);
   }
   else
      if( (errmsg = string_to_double( item2->value, &y )) ) {
         DEBUG_MSG("Failed to convert item 2 to double.");
         DEBUG_MSG(errmsg);
      }
      else
        if( x >= y )
           return( TRUE );
        else
           return( FALSE );
   return( FALSE );
} /* real_greater_or_equal */

/* str_strip
 * Use the tolower function to fold an entire string to lower case
 * and remove characters not in a-z.
 */
char    *str_strip(
char *  string)
{
   register char *      rtn_string = NULL;
   register char         local_char;
   register int         i;
   register int         j;
   char *allowed_char = "abcdefghijklmnopqrstuvwxyz0123456789_";

if(strip_stream) {
   if( (rtn_string = (char *)malloc( (strlen(string) +1) * sizeof(char) )) ) {
     j = 0;
     for( i = 0; i < strlen(string); i++) {
  	local_char = tolower( string[i]); 
	if( strchr(allowed_char, local_char) )
	rtn_string[j++] = local_char ;  
	}
     rtn_string[j] = '\0';
   }
   else
      ERROR_MSG("str_strip: Out of memory.");
   return( rtn_string );
   }
else return( string);
} /* str_strip */
