package containers import ( "errors" ) var ErrInvalidQueue = errors.New("invalid queue") var ErrEmptyQueue = errors.New("empty queue") 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 ErrInvalidQueue } 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, ErrInvalidQueue } if q.IsEmpty() { return nil, ErrEmptyQueue } 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, ErrInvalidQueue } if q.IsEmpty() { return nil, ErrEmptyQueue } return q.head.Elem, nil } func BuildQueue[T any]() *Queue[T] { empty := emptyElem[T]() return &Queue[T]{ head: empty, tail: empty, } }