Moved stuff, added Contains to sliceutils

This commit is contained in:
mStar aka a person 2024-01-17 09:47:33 +01:00
parent 94e1712122
commit ae2da5efa5
10 changed files with 21 additions and 43 deletions

34
containers/generics.go Normal file
View file

@ -0,0 +1,34 @@
package containers
type ChainElem[T any] struct {
Elem *T
Next *ChainElem[T]
}
// reachable checks if you can reach elem l when starting from elem f.
// It detects loops and returns false if it runs into one.
func reachable[T any](f, l *ChainElem[T]) bool {
// Map to keep track of nodes already visited
checks := make(map[*ChainElem[T]]bool)
for w := f; w != l; w = w.Next {
if w == nil {
return false
}
// Shortcut: Maps where the value is bool have a default value of false
// If a key doesn't exist yet the result will thus be false
if checks[w] {
return false
}
// Set the current elem to true to mark it as visited
checks[w] = true
}
return true
}
// emptyElem creates a new ChainElem[T] with empty values.
func emptyElem[T any]() *ChainElem[T] {
return &ChainElem[T]{
Elem: nil,
Next: nil,
}
}

95
containers/queues.go Normal file
View file

@ -0,0 +1,95 @@
package containers
import (
"encoding/json"
"errors"
)
type Queue[T any] struct {
head *ChainElem[T]
tail *ChainElem[T]
}
// isValid checks if the queue is still valid.
func (q *Queue[T]) isValid() bool {
return q.head != nil &&
q.tail != nil &&
reachable(q.head, q.tail)
}
// IsEmpty checks if the queue is empty.
func (q *Queue[T]) IsEmpty() bool {
return q.head == q.tail
}
// Push adds a new element to the end of the queue.
func (q *Queue[T]) Push(elem *T) error {
if !q.isValid() {
return errors.New("invalid queue")
}
e := emptyElem[T]()
q.tail.Elem = elem
q.tail.Next = e
q.tail = e
return nil
}
// Pop removes the first element of the queue.
// It errors out if there is no element or the queue is invalid.
func (q *Queue[T]) Pop() (*T, error) {
if !q.isValid() {
return nil, errors.New("invalid queue")
}
if q.IsEmpty() {
return nil, errors.New("empty queue")
}
Elem := q.head.Elem
q.head = q.head.Next
return Elem, nil
}
// Top returns the first element of the queue without removing it.
// It errors out if there is no element or the queue is invalid.
func (q *Queue[T]) Top() (*T, error) {
if !q.isValid() {
return nil, errors.New("queue invalid")
}
if q.IsEmpty() {
return nil, errors.New("queue empty")
}
return q.head.Elem, nil
}
// HeadElem returns the first ChainElem of the queue without removing it.
// It errors out if there is no element or the queue is invalid.
func (q *Queue[T]) HeadElem() (*ChainElem[T], error) {
if !q.isValid() {
return nil, errors.New("queue invalid")
}
if q.IsEmpty() {
return nil, errors.New("queue empty")
}
return q.head, nil
}
// MarshalJSON is used for generating json data when using json.Marshal.
func (q *Queue[T]) MarshalJSON() ([]byte, error) {
if !q.isValid() {
return nil, errors.New("queue invalid")
}
if q.IsEmpty() {
return nil, errors.New("queue empty")
}
return json.Marshal(q.head)
}
func BuildQueue[T any]() *Queue[T] {
empty := emptyElem[T]()
return &Queue[T]{
head: empty,
tail: empty,
}
}

81
containers/stacks.go Normal file
View file

@ -0,0 +1,81 @@
package containers
import (
"encoding/json"
"errors"
)
type Stack[T any] struct {
top *ChainElem[T]
bottom *ChainElem[T]
}
// isValid checks if the stack is valid.
func (s *Stack[T]) isValid() bool {
return s.top != nil &&
s.bottom != nil &&
reachable(s.top, s.bottom)
}
// IsEmpty checks if the stack is currently empty.
func (s *Stack[T]) IsEmpty() bool {
return s.top == s.bottom
}
// Push adds a new element to the top of the stack.
// It errors out if the stack is invalid.
func (s *Stack[T]) Push(e *T) error {
if !s.isValid() {
return errors.New("stack invalid")
}
n := emptyElem[T]()
n.Elem = e
n.Next = s.top
s.top = n
return nil
}
// Pop removes the first element at the top of the stack and returns it.
// It errors out if the stack is invalid or empty.
func (s *Stack[T]) Pop() (*T, error) {
if !s.isValid() {
return nil, errors.New("stack invalid")
}
if s.IsEmpty() {
return nil, errors.New("stack empty")
}
e := s.top.Elem
s.top = s.top.Next
return e, nil
}
// Top returns the first element at the top of the stack without removing it.
// It errors out if the stack is empty or invalid.
func (s *Stack[T]) Top() (*T, error) {
if !s.isValid() {
return nil, errors.New("stack invalid")
}
if s.IsEmpty() {
return nil, errors.New("stack empty")
}
return s.top.Elem, nil
}
// MarshalJSON is used by json.Marshal to create a json representation.
func (s *Stack[T]) MarshalJSON() ([]byte, error) {
if !s.isValid() {
return nil, errors.New("queue invalid")
}
if s.IsEmpty() {
return nil, errors.New("queue empty")
}
return json.Marshal(s.top)
}
func BuildStack[T any]() *Stack[T] {
empty := emptyElem[T]()
return &Stack[T]{
top: empty,
bottom: empty,
}
}