/*
 * File: oset.c
 *  Contents: compl, diff, inter, unions
 */

#include "..\h\config.h"
#include "..\h\rt.h"
#include "rproto.h"


/*
 * ~x - complement cset x.
 */

OpDcl(compl,1,"~")
   {
   register int i;
   union block *bp;
   int *cs, csbuf[CsetSize];

   if (blkreq((word)sizeof(struct b_cset)) == Error)
      RunErr(0, NULL);

   /*
    * Arg1 must be a cset.
    */
   if (cvcset(&Arg1, &cs, csbuf) == CvtFail) 
      RunErr(104, &Arg1);

   /*
    * Allocate a new cset and then copy each cset word from Arg1 
    *  into the new cset words, complementing each bit.
    */
   bp = (union block *)alccset();
   for (i = 0; i < CsetSize; i++) 
       bp->cset.bits[i] = ~cs[i];
   Arg0.dword = D_Cset;
   BlkLoc(Arg0) = bp;
   Return;
   }

/*
 * x -- y - difference of csets x and y or of sets x and y.
 */

OpDcl(diff,2,"--")
   {
   register word i;
   word slotnum;
   register union block *srcp, *tstp, *dstp, **hook;
   int *cs1, *cs2, csbuf1[CsetSize], csbuf2[CsetSize], res;
   struct b_slots *seg;
   struct b_selem *ep;

   if (Qual(Arg1) || Qual(Arg2))
      goto skipsets;
   if (Arg1.dword == D_Set && Arg2.dword != D_Set) 
      RunErr(119, &Arg2);
   if (Arg2.dword == D_Set && Arg1.dword != D_Set) 
      RunErr(119, &Arg1);
   if (Arg1.dword == D_Set && Arg2.dword == D_Set) {
      /*
       * Both Arg1 and Arg2 are sets - do set difference.  Make a new set
       *  based on the size of Arg1.
       */
      dstp = hmake(T_Set, (word)0, BlkLoc(Arg1)->set.size);
      if (dstp == NULL)
         RunErr(0, NULL);
      /*
       * For each element in set Arg1 if it is not in set Arg2
       *  copy it directly into the result set.
       */
      srcp = BlkLoc(Arg1);
      tstp = BlkLoc(Arg2);
      for (i = 0; i < HSegs && (seg = srcp->set.hdir[i]) != NULL; i++)
         for (slotnum = segsize[i] - 1; slotnum >= 0; slotnum--) {
            ep = (struct b_selem *)seg->hslots[slotnum];
            while (ep != NULL) {
               memb(tstp, &ep->setmem, ep->hashnum, &res);
               if (res == 0) {
                  hook = memb(dstp, &ep->setmem, ep->hashnum, &res);
                  addmem(&dstp->set, alcselem(&ep->setmem, ep->hashnum), hook);
                  }
               ep = (struct b_selem *)ep->clink;
               }
            }
      Arg0.dword = D_Set;
      BlkLoc(Arg0) = dstp;
      if (TooSparse(dstp))
         hshrink(&Arg0);
      }
   else {
      skipsets:
   if (blkreq((word)sizeof(struct b_cset)) == Error)
      RunErr(0, NULL);

   /*
    * Arg1 and Arg2 must be csets.
    */
   if (cvcset(&Arg1, &cs1, csbuf1) == CvtFail) 
      RunErr(120, &Arg1);
   if (cvcset(&Arg2, &cs2, csbuf2) == CvtFail) 
      RunErr(120, &Arg2);

   /*
    * Allocate a new cset and in each word of it, compute the value
    *  of the bitwise difference of the corresponding words in the
    *  Arg1 and Arg2 csets.
    */
   dstp = (union block *)alccset();
   for (i = 0; i < CsetSize; i++) {
      dstp->cset.bits[i] = cs1[i] & ~cs2[i];
      }

   Arg0.dword = D_Cset;
   BlkLoc(Arg0) = dstp;
   }
   Return;
   }

/*
 * x ** y - intersection of csets x and y or of sets x and y.
 */

OpDcl(inter,2,"**")
   {
   register word i;
   word slotnum;
   register union block *srcp, *tstp, *dstp, **hook;
   int *cs1, *cs2, csbuf1[CsetSize], csbuf2[CsetSize], res;
   struct b_slots *seg;
   struct b_selem *ep;

   if (Qual(Arg1) || Qual(Arg2))
      goto skipsets;
   if (Arg1.dword == D_Set && Arg2.dword != D_Set) 
      RunErr(119, &Arg2);
   if (Arg2.dword == D_Set && Arg1.dword != D_Set) 
      RunErr(119, &Arg1);
   if (Arg1.dword == D_Set && Arg2.dword == D_Set) {
      /*
       * Both Arg1 and Arg2 are sets - do set intersection.
       *  Make a new set the size of the smaller argument set.
       */
      dstp = hmake(T_Set, (word)0,
         Min(BlkLoc(Arg1)->set.size, BlkLoc(Arg2)->set.size));
      if (dstp == NULL)
         RunErr(0, NULL);
      /*
       * Using the smaller of the two sets as the source
       *  copy directly into the result each of its elements
       *  that are also members of the other set.
       */
      if (BlkLoc(Arg1)->set.size <= BlkLoc(Arg2)->set.size) {
         srcp = BlkLoc(Arg1);
         tstp = BlkLoc(Arg2);
         }
      else {
         srcp = BlkLoc(Arg2);
         tstp = BlkLoc(Arg1);
         }
      for (i = 0; i < HSegs && (seg = srcp->set.hdir[i]) != NULL; i++)
         for (slotnum = segsize[i] - 1; slotnum >= 0; slotnum--) {
            ep = (struct b_selem *)seg->hslots[slotnum];
            while (ep != NULL) {
               memb(tstp, &ep->setmem, ep->hashnum, &res);
               if (res != 0) {
                  hook = memb(dstp, &ep->setmem, ep->hashnum, &res);
                  addmem(&dstp->set, alcselem(&ep->setmem, ep->hashnum), hook);
                  }
               ep = (struct b_selem *)ep->clink;
               }
            }
      Arg0.dword = D_Set;
      BlkLoc(Arg0) = dstp;
      if (TooSparse(dstp))
         hshrink(&Arg0);
      }
   else {
      skipsets:
   if (blkreq((word)sizeof(struct b_cset)) == Error)
      RunErr(0, NULL);

   /*
    * Arg1 and Arg2 must be csets.
    */
   if (cvcset(&Arg1, &cs1, csbuf1) == CvtFail) 
      RunErr(120, &Arg1);
   if (cvcset(&Arg2, &cs2, csbuf2) == CvtFail) 
      RunErr(120, &Arg2);

   /*
    * Allocate a new cset and in each word of it, compute the value
    *  of the bitwise intersection of the corresponding words in the
    *  Arg1 and Arg2 csets.
    */
   dstp = (union block *)alccset();
   for (i = 0; i < CsetSize; i++) {
      dstp->cset.bits[i] = cs1[i] & cs2[i];
      }

   Arg0.dword = D_Cset;
   BlkLoc(Arg0) = dstp;
   }
   Return;
   }

/*
 * x ++ y - union of csets x and y or of sets x and y.
 */

OpDcl(unions,2,"++")
   {
   register word i;
   word slotnum;
   register union block *srcp, *tstp, *dstp, **hook;
   int *cs1, *cs2, csbuf1[CsetSize], csbuf2[CsetSize], res;
   struct b_slots *seg;
   struct b_selem *ep;
   dptr srcd, tstd;

   if (Qual(Arg1) || Qual(Arg2))
      goto skipsets;
   if (Arg1.dword == D_Set && Arg2.dword != D_Set) 
      RunErr(119, &Arg2);
   if (Arg2.dword == D_Set && Arg1.dword != D_Set) 
      RunErr(119, &Arg1);
   if (Arg1.dword == D_Set && Arg2.dword == D_Set) {
      /*
       * Both Arg1 and Arg2 are sets - do set union.  Copy the larger set
       *  and ensure there's room for *Arg1 + *Arg2 elements.
       */
      if (BlkLoc(Arg1)->set.size >= BlkLoc(Arg2)->set.size) {
         srcd = &Arg1;
         tstd = &Arg2;
         }
      else {
         srcd = &Arg2;
         tstd = &Arg1;
         }
      if (cpset(srcd, &Arg0, BlkLoc(Arg1)->set.size + BlkLoc(Arg2)->set.size)
            == Error)
         RunErr(0, NULL);
      /*
       * Copy each element from the smaller set into the result set,
       *  if it is not already there.
       */
      srcp = BlkLoc(*srcd);
      tstp = BlkLoc(*tstd);
      dstp = BlkLoc(Arg0);
      for (i = 0; i < HSegs && (seg = tstp->set.hdir[i]) != NULL; i++)
         for (slotnum = segsize[i] - 1; slotnum >= 0; slotnum--) {
            ep = (struct b_selem *)seg->hslots[slotnum];
            while (ep != NULL) {
               hook = memb(dstp, &ep->setmem, ep->hashnum, &res);
               if (res == 0)
                  addmem(&dstp->set, alcselem(&ep->setmem, ep->hashnum), hook);
               ep = (struct b_selem *)ep->clink;
            }
         }
      if (TooCrowded(dstp))		/* if the union got too big, enlarge */
         hgrow(&Arg0);
      }
   else {
      skipsets:

   if (blkreq((word)sizeof(struct b_cset)) == Error)
      RunErr(0, NULL);

   /*
    * Arg1 and Arg2 must be csets.
    */
   if (cvcset(&Arg1, &cs1, csbuf1) == CvtFail) 
      RunErr(120, &Arg1);
   if (cvcset(&Arg2, &cs2, csbuf2) == CvtFail) 
      RunErr(120, &Arg2);

   /*
    * Allocate a new cset and in each word of it, compute the value
    *  of the bitwise union of the corresponding words in the
    *  Arg1 and Arg2 csets.
    */
   dstp = (union block *)alccset();
   for (i = 0; i < CsetSize; i++) {
      dstp->cset.bits[i] = cs1[i] | cs2[i];
      }

   Arg0.dword = D_Cset;
   BlkLoc(Arg0) = dstp;
   }
   Return;
   }
