/* Copyright © 2023 Thomas von Dein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package main import ( "container/list" "fmt" "sync" ) // The stack uses a linked list provided by container/list as storage // and works after the LIFO principle (last in first out). Most of the // work is being done in the linked list, but we add a couple of // cenvenient functions, so that the user doesn't have to cope with // list directly. type Stack struct { linklist list.List backup list.List debug bool rev int backuprev int mutex sync.Mutex } // FIXME: maybe use a separate stack object for backup so that it has // its own revision etc func NewStack() *Stack { return &Stack{ linklist: list.List{}, backup: list.List{}, rev: 0, backuprev: 0, } } func (s *Stack) Debug(msg string) { if s.debug { fmt.Printf("DEBUG(%03d): %s\n", s.rev, msg) } } func (s *Stack) ToggleDebug() { s.debug = !s.debug } func (s *Stack) Bump() { s.rev++ } // append an item to the stack func (s *Stack) Push(x float64) { s.mutex.Lock() defer s.mutex.Unlock() s.Debug(fmt.Sprintf(" push to stack: %.2f", x)) s.Bump() s.linklist.PushBack(x) } // remove and return an item from the stack func (s *Stack) Pop() float64 { s.mutex.Lock() defer s.mutex.Unlock() if s.linklist.Len() == 0 { return 0 } tail := s.linklist.Back() val := tail.Value s.linklist.Remove(tail) s.Debug(fmt.Sprintf(" remove from stack: %.2f", val)) s.Bump() return val.(float64) } // just remove the last item, do not return it func (s *Stack) Shift(num ...int) { s.mutex.Lock() defer s.mutex.Unlock() count := 1 if len(num) > 0 { count = num[0] } if s.linklist.Len() == 0 { return } for i := 0; i < count; i++ { tail := s.linklist.Back() s.linklist.Remove(tail) s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value)) } } // Return the last num items from the stack w/o modifying it. func (s *Stack) Last(num ...int) []float64 { items := []float64{} i := s.Len() count := 1 if len(num) > 0 { count = num[0] } for e := s.linklist.Front(); e != nil; e = e.Next() { if i <= count { items = append(items, e.Value.(float64)) } i-- } return items } // Return all elements of the stack without modifying it. func (s *Stack) All() []float64 { items := []float64{} for e := s.linklist.Front(); e != nil; e = e.Next() { items = append(items, e.Value.(float64)) } return items } // dump the stack to stdout, including backup if debug is enabled func (s *Stack) Dump() { fmt.Printf("Stack revision %d (%p):\n", s.rev, &s.linklist) for e := s.linklist.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) } if s.debug { fmt.Printf("Backup stack revision %d (%p):\n", s.backuprev, &s.backup) for e := s.backup.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) } } } func (s *Stack) Clear() { s.Debug("DEBUG: clearing stack") s.linklist = list.List{} } func (s *Stack) Len() int { return s.linklist.Len() } func (s *Stack) Backup() { // we need clean the list and restore it from scratch each time we // make a backup, because the elements in list.List{} are pointers // and lead to unexpected results. The methid here works reliably // at least. s.backup = list.List{} for e := s.linklist.Front(); e != nil; e = e.Next() { s.backup.PushBack(e.Value) } s.backuprev = s.rev } func (s *Stack) Restore() { if s.rev == 0 { fmt.Println("error: stack is empty.") return } s.Debug(fmt.Sprintf("restoring stack to revision %d", s.backuprev)) s.rev = s.backuprev s.linklist = s.backup } func (s *Stack) Reverse() { newstack := list.List{} for e := s.linklist.Front(); e != nil; e = e.Next() { newstack.PushFront(e.Value) } s.linklist = newstack }