/* Copyright (c) 2000 by Kevin Forchione.  All Rights Reserved. */
/*
*  TADS LIBRARY EXTENSION
*  STACK.T
*  version 1.0
*
*      stack.t implements a dynamically created FIFO or LIFO stack and a
*  Vector class that can be used for list manipulation.
*
*----------------------------------------------------------------------
*  REQUIREMENTS
*
*      + HTML TADS 2.2.6 or later
*
*----------------------------------------------------------------------
*  IMPORTANT LIBRARY INTERFACE AND MODIFICATION
*
*      None.
*
*----------------------------------------------------------------------
*  COPYRIGHT NOTICE
*
*      You may modify and use this file in any way you want, provided that
*              if you redistribute modified copies of this file in source form, the
*      copies must include the original copyright notice (including this
*      paragraph), and must be clearly marked as modified from the original
*      version.
*
*------------------------------------------------------------------------------
*  REVISION HISTORY
*
*              08-Mar-00:      Creation.
*/

#define __STACK_MODULE_

#pragma C+

/*
*  Stack: object
*
*  This implements a FIFO (First-In-First-Out) or LIFO
*  (Last-In-First-Out) stack that can be used for pushing and popping.
*  The Stack will automatically create a Vector class object, which it
*  uses for keeping track of pushed objects.
*
*  To use a stack simply create it dynamically. The Stack default is
*  LIFO. If you wish it to behave as a FIFO stack simply set its isLIFO
*  attribute to nil.
*
*      local stack = new Stack;
*      stack.isLIFO = nil;
*
*  You can push items onto the stack with the stack's push method:
*
*      stack.push(item);
*
*  and pop items off the stack using its pop method:
*
*      item = stack.pop;
*
*  When the stack is no longer needed you can delete it with the delete
*  statement. The stack will automatically clean-up the Vector class
*  object that it dynamically created.
*/
class Stack: object
   isLIFO = true
   vector = nil    // pointer to dynamically-created Vector class obj
   /*
    *  Push an item onto the stack. All items are pushed to the top of
    *  the stack.
    */
   push(item) = {
       self.vector.addElement(item);
   }
   /*
    *  Push an item to the bottom of the stack.
    */
   pushB(item) = {
       self.vector.addElementToHead(item);
   }
   /*
    *  Pop an item off the stack. If stack.isLIFO then it pops the
    *  item from the top of the stack; otherwise it pops the item from
    *  the bottom of the stack.
    */
   pop = {
       local item;
       if (self.isLIFO)
       {
           item = self.vector.elementAt(self.vector.getSize);
           self.vector.removeElementAt(self.vector.getSize);
       }
       else
       {
           item = self.vector.elementAt(1);
           self.vector.removeElementAt(1);
       }
       return item;
   }
   /*
    *  Returns a boolean indicating whether the stack is empty or not.
    */
   isEmpty = {
       return self.vector.isEmpty;
   }
   /*
    *  Returns the size of the stack.
    */
   getSize = {
       return self.vector.getSize;
   }
   construct = {self.vector = new Vector;}
   destruct = {delete self.vector;}
;

/*
*  Vector: object
*
*  The Vector class can be used to manipulate lists.
*/
class Vector: object
   list = []
   /*
    *  Add an element to the tail of the list.
    */
   addElement(item) = {
       self.list += item;
   }
   /*
    *  Add an element to the head of the list.
    */
   addElementToHead(item) = {
       local newlist = [];

       newlist += item;
       newlist += self.list;
       self.list = newlist;
   }
   /*
    *  Add item to the list at pos n. If n > list.getSize the list
    *  is padded with nil values to a length of n-1 before adding item.
    */
   addElementAt(item, n) = {
       local newlist = [], l1 = [], l2 = [];

       if (n > self.getSize) self.padVector(n-1);

       newlist += self.sublist(1, n-1);
       newlist += item;
       newlist += self.sublist(n, self.getSize);
       self.list = newlist;
   }
   /*
    *  Pad the list with nil elements to the length of n.
    */
   padVector(n) = {
       local i, len;

       len = self.getSize + 1;
       for (i = len; i <= n; ++i)
           self.list += nil;
   }
   /*
    *  Left pad the list with nil elements to the length of n.
    */
   lpadVector(n) = {
       local i, len, newlist = [];

       len = n - self.getSize;
       for (i = 1; i <= len; ++i)
           newlist += nil;

       newlist += self.list;
       self.list = newlist;
   }
   /*
    *  Remove the element at pos n in the list.
    */
   removeElementAt(n) = {
       local i, len, newlist = [];

       len = self.getSize;
       for (i = 1; i <= len; ++i)
       {
           if (i != n)
               newlist += self.list[i];
       }
       self.list = newlist;
   }
   /*
    *  Remove the first occurrence (from left to right) of item from
    *  the list.
    */
   removeFirstOccurrence(item) = {
       local n = self.posFirstOccurrence(item);

       if (0 < n && n <= self.getSize)
           self.removeElementAt(n);
   }
   /*
    *  Remove the last occurrence (from left to right) of item from
    *  the list.
    */
   removeLastOccurrence(item) = {
       local n = self.posLastOccurrence(item);

       if (0 < n && n <= self.getSize)
           self.removeElementAt(n);
   }
   /*
    *  Remove all occurrences of item from the list.
    */
   removeAllOccurrence(item) = {
       self.list -= [item];
   }
   elementAt(n) = {
       if (0 < n && n <= self.getSize)
           return self.list[n];
       else return nil;
   }
   /*
    *  Return the pos n for the first occurrence (from left to right) of
    *  item in the list.
    */
   posFirstOccurrence(item) = {
       local f = find(self.list, item);
       if (f) return f;
       return 0;
   }
   /*
    *  Return the pos n for the last occurrence (from left to right) of
    *  item in the list.
    */
   posLastOccurrence(item) = {
       local i, len;

       for (i = length(self.list); i > 0; --i)
       {
           if (self.list[i] == item)
               return i;
       }
       return 0;
   }
   /*
    *  Return a list of elements that are positions in the list for
    *  all occurrences of item.
    */
   posAllOccurrence(item) = {
       local i, len, newlist = [];

       len = length(self.list);
       for (i = 1; i <= len; ++i)
       {
           if (self.list[i] == item)
               newlist += [i];
       }
       return newlist;
   }
   /*
    *  Return a count of all occurrences of item in the list.
    */
   countOccurrence(item) = {
       local n = self.posAllOccurrence(item);

       return length(n);
   }
   /*
    *  Return a sublist of list, starting from pos for a length len
    *  (if provided). if len is not provided then it is the length of
    *  the remainder of the list. If len is greater than the length
    *  of the list then the sublist contains the remainder of the list.
    *  If pos is greater than the length of the list then an empty list
    *  is returned.
    */
   sublist(pos, ...) = {
       local i, len, newlist = [];

       if (argcount > 1) len = getarg(2);
       if (len == nil) len = self.getSize - pos + 1;

       for (i = pos; i <= pos + len - 1; ++i)
       {
           if (self.getSize >= i)
               newlist += self.list[i];
           else break;
       }
       return newlist;
   }
   /*
    *  Returns the length of the list.
    */
   getSize = {return length(self.list);}
   /*
    *  Returns a boolean indicating whether the list is empty or not.
    */
   isEmpty = {
       if (self.getSize)
           return nil;
       else return true;
   }
   construct = {}
   destruct = {}
;

#pragma C-