/* 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-
