;+
; Function: csstack
;
; Purpose:  This procedure implements the push,pop,& peek
;           methods for a traditional computer science 
;           data structure: the stack.  It is a basic
;           LIFO data structure.
;           
;           Advantages over array:
;           1. store heterogenous elements of any type in a stack.
;           2. stack can grow as large as memory and you don't
;           need to know how big it will be in advance
;           3. You don't need to worry about how the data is stored
;           
;           Disadvantages over array:
;           1. You can't directly apply operations to the data
;           structure
;           2. You are forced to use abstraction
;
;
;           Inputs: arg1:the meaning of the argument varies with syntax
;                   arg2:the meaning of the argument varies with syntax
;
;           Keywords: push(optional) : set this to add an item to the stack and return the modified stack
;                     pop(optional) : set this to remove an item from the stack and return the modified stack
;                     peek(optional) : set this to return the top element on the stack
;                     length(optional): set this if you want
;                                       to return the length          
;                     free(optional): set this if you want to free the
;                                     vector's memory without
;                                     creating a leak, it will return
;                                     the number of elements free'd
;                                    
;                    If no keywords are set, default behavior is push
;                    
;      
;           Outputs: If push or pop are set it returns the modified stack
;                    If peek is set it returns the top element on the stack
;                    If length or free are set it returns a number of items
;
;           Syntax(each method is followed by examples): 
;
;                   push
;                    stk = csstack(item)
;                    stk = csstack(item,stk)  ;stk can be defined or not
;                    stk = csstack(item,stk,/push)
;                   pop:
;                    stk = csstack(stk,/pop) ;must have at least one element 
;                   peek:
;                    item = csstack(stk,/peek) ;must have at least one element
;                   length: 
;                     length = csvector(stk,/length)
;                   free:
;                     num = csvector(stk,/free)
;                     
;       NOTES: in the event of overflow during add the vector.a
;       component will double in size
;
;       Push stores a copy of the element not the element itself
; 
;       If you want to do manual lengths and reads you can look
;       at the code, but I would recommend against cause you are
;       violating abstraction which means the internal representation
;       could change and invalidate your code.
;
;       This might be worth writing in O.O. idl as well
;
;       To get type flexibility it uses a pointer for every object
;       Thus if you aren't careful this function will eat your
;       system memory for breakfast.  Use heap_gc to clean up if you 
;       are running out of memory.
;
; 
; $LastChangedBy: pcruce $
; $LastChangedDate: 2008-09-12 16:21:16 -0700 (Fri, 12 Sep 2008) $
; $LastChangedRevision: 3487 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/ssl_general/trunk/mini/csstack.pro $
;-
;
;-


function csstack,arg1,arg2,push=push,pop=pop,peek=peek,length=length,free=free

if keyword_set(length) then begin ;length

   if ~keyword_set(arg1) then begin
      message,'Illegal syntax(arg1 must be set)'
   endif

   if keyword_set(arg2) then begin
      message,'Illegal syntax(arg2 and length are set)'
   endif

   if keyword_set(push) then begin
      message,'Illegal syntax(length and push are set)'
   endif
   
   if keyword_set(pop) then begin
      message,'Illegal syntax(length and pop are set)'
   endif
   
   if keyword_set(peek) then begin
      message,'Illegal syntax(length and peek are set)'
   endif

   if keyword_set(free) then begin
      message,'Illegal syntax(length and free are set)'
   endif

   return, arg1.l

endif else if keyword_set(free) then begin ;free

   if ~keyword_set(arg1) then begin
      message,'Illegal syntax(arg1 must be set)'
   endif

   if keyword_set(arg2) then begin
      message,'Illegal syntax(arg2 and free are set)'
   endif

   if keyword_set(push) then begin
      message,'Illegal syntax(free and push are set)'
   endif
   
   if keyword_set(pop) then begin
      message,'Illegal syntax(free and pop are set)'
   endif
   
   if keyword_set(peek) then begin
      message,'Illegal syntax(free and peek are set)'
   endif

   ptr_free,arg1.d

   return,arg1.l

endif else if keyword_set(peek) then begin

   if ~keyword_set(arg1) then begin
      message,'Illegal syntax(arg1 must be set)'
   endif
   
   if keyword_set(arg2) then begin
      message,'Illegal syntax(arg2 and peek are set)'
   endif
     
   if keyword_set(push) then begin
      message,'Illegal syntax(peek and push are set)'
   endif
   
   if keyword_set(pop) then begin
      message,'Illegal syntax(peek and pop are set)'
   endif
   
   if arg1.l le 0 then begin
      message,'Cannot peek empty stack'
   endif
   
   return,*(arg1.d[arg1.l-1L])
   
endif else if keyword_set(pop) then begin

   if ~keyword_set(arg1) then begin
      message,'Illegal syntax(arg1 must be set)'
   endif
   
   if keyword_set(arg2) then begin
      message,'Illegal syntax(arg2 and pop are set)'
   endif
   
   if keyword_set(push) then begin
      message,'Illegal syntax(pop and push are set)'
   endif
   
   if arg1.l le 0 then begin
      message,'Cannot pop empty stack'
   endif
   
   arg1.l--
   
   ptr_free,arg1.d[arg1.l]
   
   return,arg1
   
endif else begin ;push

  if size(arg1,/type) eq 0 then begin
    message,'Illegal syntax(arg1 must be set)'
  endif
   
  data = ptr_new(arg1)

  if ~keyword_set(arg2) then begin ;new stack

    return, {d:[data],l:1L}
    
  endif else begin
  
    if arg2.l eq n_elements(arg2.d) then begin

      out = {d:replicate(data,arg2.l*2L),l:arg2.l}

      out.d[0:arg2.l-1L] = arg2.d[0L:arg2.l-1L]

    endif else begin

      out = arg2

    endelse

    out.d[arg2.l] = data

    out.l++

    return,out
    
  endelse

endelse

end
