fn: add generic version of List

This commit is contained in:
Keagan McClelland 2024-04-29 17:41:45 -07:00
parent fb1437cb6d
commit 364d79e552
No known key found for this signature in database
GPG key ID: FA7E65C951F12439
2 changed files with 1031 additions and 0 deletions

302
fn/list.go Normal file
View file

@ -0,0 +1,302 @@
// Copyright (c) 2009 The Go Authors. All rights reserved.
// Copyright (c) 2024 Lightning Labs and the Lightning Network Developers
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package fn
type Node[A any] struct {
// prev is a pointer to the previous node in the List.
prev *Node[A]
// next is a pointer to the next node in the List.
next *Node[A]
// list is the root pointer to the List in which this node is located.
list *List[A]
// Value is the actual data contained within the Node.
Value A
}
// Next returns the next list node or nil.
func (e *Node[A]) Next() *Node[A] {
if e.list == nil {
return nil
}
if e.next == &e.list.root {
return nil
}
return e.next
}
// Prev returns the previous list node or nil.
func (e *Node[A]) Prev() *Node[A] {
if e.list == nil {
return nil
}
if e.prev == &e.list.root {
return nil
}
return e.prev
}
// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List[A any] struct {
// root is a sentinal Node to identify the head and tail of the list.
// root.prev is the tail, root.next is the head. For the purposes of
// elegance, the absence of a next or prev node is encoded as the
// address of the root node.
root Node[A]
// len is the current length of the list.
len int
}
// Init intializes or clears the List l.
func (l *List[A]) Init() *List[A] {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
return l
}
// lazyInit lazily initializes a zero List value. It is called by other public
// functions that could feasibly be called on a List that was initialized by the
// raw List[A]{} constructor.
func (l *List[A]) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
// insert inserts n after predecessor, increments l.len, and returns n.
func (l *List[A]) insert(n *Node[A], predecessor *Node[A]) *Node[A] {
// Make n point to correct neighborhood.
n.prev = predecessor
n.next = predecessor.next
// Make neighborhood point to n.
n.prev.next = n
n.next.prev = n
// Make n part of the list.
n.list = l
// Increment list length.
l.len++
return n
}
// insertVal is a convenience wrapper for
// insert(&Node[A]{Value: v}, predecessor).
func (l *List[A]) insertVal(a A, predecessor *Node[A]) *Node[A] {
return l.insert(&Node[A]{Value: a}, predecessor)
}
// move removes n from its current position and inserts it as the successor to
// predecessor.
func (l *List[A]) move(n *Node[A], predecessor *Node[A]) {
if n == predecessor {
return // Can't move after itself.
}
if predecessor.next == n {
return // Nothing to be done.
}
// Bind previous and next to each other.
n.prev.next = n.next
n.next.prev = n.prev
// Make n point to new neighborhood.
n.prev = predecessor
n.next = predecessor.next
// Make new neighborhood point to n.
n.prev.next = n
n.next.prev = n
}
// New returns an initialized List.
func NewList[A any]() *List[A] {
l := List[A]{}
return l.Init()
}
// Len returns the number of elements of List l.
// The complexity is O(1).
func (l *List[A]) Len() int {
return l.len
}
// Front returns the first Node of List l or nil if the list is empty.
func (l *List[A]) Front() *Node[A] {
if l.len == 0 {
return nil
}
return l.root.next
}
// Back returns the last Node of List l or nil if the list is empty.
func (l *List[A]) Back() *Node[A] {
if l.len == 0 {
return nil
}
return l.root.prev
}
// Remove removes Node n from List l if n is an element of List l.
// It returns the Node value e.Value.
// The Node must not be nil.
func (l *List[A]) Remove(n *Node[A]) A {
if n.list == l {
n.prev.next = n.next
n.next.prev = n.prev
l.len--
v := n.Value
// Set all node data to nil to prevent dangling references.
*n = Node[A]{Value: v}
return v
}
return n.Value
}
// PushFront inserts a new Node n with value a at the front of List l and
// returns n.
func (l *List[A]) PushFront(a A) *Node[A] {
l.lazyInit()
return l.insertVal(a, &l.root)
}
// PushBack inserts a new Node n with value a at the back of List l and returns
// n.
func (l *List[A]) PushBack(a A) *Node[A] {
l.lazyInit()
return l.insertVal(a, l.root.prev)
}
// InsertBefore inserts a new Node n with value a immediately before successor
// and returns n. If successor is not an element of l, the list is not
// modified. The successor must not be nil.
func (l *List[A]) InsertBefore(a A, successor *Node[A]) *Node[A] {
if successor == nil {
return l.insertVal(a, &l.root)
}
if successor.list != l {
return nil
}
return l.insertVal(a, successor.prev)
}
// InsertAfter inserts a new Node n with value a immediately after and returns
// e. If predecessor is not an element of l, the list is not modified. The
// predecessor must not be nil.
func (l *List[A]) InsertAfter(a A, predecessor *Node[A]) *Node[A] {
if predecessor == nil {
return l.insertVal(a, l.root.prev)
}
if predecessor.list != l {
return nil
}
return l.insertVal(a, predecessor)
}
// MoveToFront moves Node n to the front of List l.
// If n is not an element of l, the list is not modified.
// The Node must not be nil.
func (l *List[A]) MoveToFront(n *Node[A]) {
if n.list == l {
l.move(n, &l.root)
}
}
// MoveToBack moves Node n to the back of List l.
// If n is not an element of l, the list is not modified.
// The Node must not be nil.
func (l *List[A]) MoveToBack(n *Node[A]) {
if n.list == l {
l.move(n, l.root.prev)
}
}
// MoveBefore moves Node n to its new position before successor.
// If n or successor is not an element of l, or n == successor, the list is not
// modified. The Node and successor must not be nil.
func (l *List[A]) MoveBefore(n, successor *Node[A]) {
if n.list == l && successor.list == l {
l.move(n, successor.prev)
}
}
// MoveAfter moves Node n to its new position after predecessor.
// If n or predecessor is not an element of l, or n == predecessor, the list is
// not modified. The Node and predecessor must not be nil.
func (l *List[A]) MoveAfter(n, predecessor *Node[A]) {
if n.list == l && predecessor.list == l {
l.move(n, predecessor)
}
}
// PushBackList inserts a copy of List other at the back of List l.
// The Lists l and other may be the same. They must not be nil.
func (l *List[A]) PushBackList(other *List[A]) {
l.lazyInit()
n := other.Front()
sz := other.Len()
for i := 0; i < sz; i++ {
l.insertVal(n.Value, l.root.prev)
n = n.Next()
}
}
// PushFrontList inserts a copy of List other at the front of List l.
// The Lists l and other may be the same. They must not be nil.
func (l *List[A]) PushFrontList(other *List[A]) {
l.lazyInit()
n := other.Back()
sz := other.Len()
for i := 0; i < sz; i++ {
l.insertVal(n.Value, &l.root)
n = n.Prev()
}
}

729
fn/list_test.go Normal file
View file

@ -0,0 +1,729 @@
package fn
import (
"math/rand"
"reflect"
"testing"
"testing/quick"
)
func GenList(r *rand.Rand) *List[uint32] {
size := int(r.Uint32() >> 24)
l := NewList[uint32]()
for i := 0; i < size; i++ {
l.PushBack(rand.Uint32())
}
return l
}
func GetRandNode(l *List[uint32], r *rand.Rand) *Node[uint32] {
if l.Len() == 0 {
return nil
}
idx := r.Uint32() % uint32(l.Len())
n := l.Front()
for i := 0; i < int(idx); i++ {
n = n.Next()
}
return n
}
func TestPushLenIncrement(t *testing.T) {
err := quick.Check(
func(l *List[uint32], x uint32, front bool) bool {
sz := l.Len()
if front {
l.PushFront(x)
} else {
l.PushBack(x)
}
sz2 := l.Len()
return sz2 == sz+1
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
vs[0] = reflect.ValueOf(GenList(r))
vs[1] = reflect.ValueOf(r.Uint32())
vs[2] = reflect.ValueOf(r.Uint32()%2 == 0)
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestRemoveLenDecrement(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32]) bool {
if l.Len() == 0 {
return true
}
sz := l.Len()
l.Remove(n)
sz2 := l.Len()
return sz2 == sz-1
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestPushGetCongruence(t *testing.T) {
err := quick.Check(
func(l *List[uint32], x uint32, front bool) bool {
if front {
l.PushFront(x)
return l.Front().Value == x
} else {
l.PushBack(x)
return l.Back().Value == x
}
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
vs[0] = reflect.ValueOf(GenList(r))
vs[1] = reflect.ValueOf(r.Uint32())
vs[2] = reflect.ValueOf(r.Uint32()%2 == 0)
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestInsertBeforeFrontIdentity(t *testing.T) {
err := quick.Check(
func(l *List[uint32], x uint32) bool {
if l == nil {
return true
}
nodeX := l.InsertBefore(x, l.Front())
return nodeX == l.Front()
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(r.Uint32())
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestInsertAfterBackIdentity(t *testing.T) {
err := quick.Check(
func(l *List[uint32], x uint32) bool {
if l == nil {
return true
}
nodeX := l.InsertAfter(x, l.Back())
return nodeX == l.Back()
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(r.Uint32())
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestInsertBeforeNextIdentity(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32], x uint32) bool {
if n == nil {
return true
}
nodeX := l.InsertBefore(x, n)
return nodeX.Next() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
vs[2] = reflect.ValueOf(r.Uint32())
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestInsertAfterPrevIdentity(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32], x uint32) bool {
if n == nil {
return true
}
nodeX := l.InsertAfter(x, n)
return nodeX.Prev() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
vs[2] = reflect.ValueOf(r.Uint32())
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestMoveToFrontFrontIdentity(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32]) bool {
if n == nil {
return true
}
l.MoveToFront(n)
return l.Front() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestMoveToBackBackIdentity(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32]) bool {
if n == nil {
return true
}
l.MoveToBack(n)
return l.Back() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestMoveBeforeFrontIsFront(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32]) bool {
if n == nil {
return true
}
l.MoveBefore(n, l.Front())
return l.Front() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestMoveAfterBackIsBack(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32]) bool {
if n == nil {
return true
}
l.MoveAfter(n, l.Back())
return l.Back() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestMultiMoveErasure(t *testing.T) {
err := quick.Check(
func(l *List[uint32], n *Node[uint32], m *Node[uint32]) bool {
if n == nil || m == nil || n == m {
return true
}
l.MoveToFront(n)
l.MoveToBack(n)
l.MoveBefore(n, m)
l.MoveAfter(n, m)
return m.Next() == n
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(GetRandNode(l, r))
vs[2] = reflect.ValueOf(GetRandNode(l, r))
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestPushListSymmetry(t *testing.T) {
copyList := func(l *List[uint32]) *List[uint32] {
c := NewList[uint32]()
for n := l.Front(); n != nil; n = n.Next() {
c.PushBack(n.Value)
}
return c
}
err := quick.Check(
func(l1 *List[uint32], l2 *List[uint32]) bool {
if l1.Len() == 0 || l2.Len() == 0 {
return true
}
l1Copy := copyList(l1)
l2Copy := copyList(l2)
l1.PushBackList(l2Copy)
l2.PushFrontList(l1Copy)
iter1 := l1.Front()
iter2 := l2.Front()
for i := 0; i < l1Copy.Len()+l2Copy.Len()-1; i++ {
if iter1.Value != iter2.Value {
return false
}
iter1 = iter1.Next()
iter2 = iter2.Next()
}
return true
},
&quick.Config{
Values: func(vs []reflect.Value, r *rand.Rand) {
l := GenList(r)
l2 := GenList(r)
vs[0] = reflect.ValueOf(l)
vs[1] = reflect.ValueOf(l2)
},
},
)
if err != nil {
t.Fatal(err)
}
}
func TestIssue4103(t *testing.T) {
l1 := NewList[int]()
l1.PushBack(1)
l1.PushBack(2)
l2 := NewList[int]()
l2.PushBack(3)
l2.PushBack(4)
e := l1.Front()
l2.Remove(e) // l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}
l1.InsertBefore(8, e)
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
}
func TestIssue6349(t *testing.T) {
l := NewList[int]()
l.PushBack(1)
l.PushBack(2)
e := l.Front()
l.Remove(e)
if e.Value != 1 {
t.Errorf("e.value = %d, want 1", e.Value)
}
if e.Next() != nil {
t.Errorf("e.Next() != nil")
}
if e.Prev() != nil {
t.Errorf("e.Prev() != nil")
}
}
func checkListLen[V any](t *testing.T, l *List[V], length int) bool {
if n := l.Len(); n != length {
t.Errorf("l.Len() = %d, want %d", n, length)
return false
}
return true
}
func checkListPointers[V any](t *testing.T, l *List[V], es []*Node[V]) {
root := &l.root
if !checkListLen(t, l, len(es)) {
return
}
// zero length lists must be the zero value or properly initialized
// (sentinel circle)
if len(es) == 0 {
if l.root.next != nil && l.root.next != root ||
l.root.prev != nil && l.root.prev != root {
t.Errorf("l.root.next = %p, l.root.prev = %p;"+
"both should both be nil or %p", l.root.next,
l.root.prev, root)
}
return
}
// len(es) > 0
// check internal and external prev/next connections
for i, e := range es {
prev := root
Prev := (*Node[V])(nil)
if i > 0 {
prev = es[i-1]
Prev = prev
}
if p := e.prev; p != prev {
t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p,
prev)
}
if p := e.Prev(); p != Prev {
t.Errorf("elt[%d](%p).Prev() = %p, want %p", i, e, p,
Prev)
}
next := root
Next := (*Node[V])(nil)
if i < len(es)-1 {
next = es[i+1]
Next = next
}
if n := e.next; n != next {
t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n,
next)
}
if n := e.Next(); n != Next {
t.Errorf("elt[%d](%p).Next() = %p, want %p", i, e, n,
Next)
}
}
}
func TestList(t *testing.T) {
l := NewList[int]()
checkListPointers(t, l, []*Node[int]{})
// Single element list
e := l.PushFront(5)
checkListPointers(t, l, []*Node[int]{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Node[int]{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Node[int]{e})
l.Remove(e)
checkListPointers(t, l, []*Node[int]{})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack(0)
checkListPointers(t, l, []*Node[int]{e1, e2, e3, e4})
l.Remove(e2)
checkListPointers(t, l, []*Node[int]{e1, e3, e4})
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Node[int]{e3, e1, e4})
l.MoveToFront(e1)
l.MoveToBack(e3) // move from middle
checkListPointers(t, l, []*Node[int]{e1, e4, e3})
l.MoveToFront(e3) // move from back
checkListPointers(t, l, []*Node[int]{e3, e1, e4})
l.MoveToFront(e3) // should be no-op
checkListPointers(t, l, []*Node[int]{e3, e1, e4})
l.MoveToBack(e3) // move from front
checkListPointers(t, l, []*Node[int]{e1, e4, e3})
l.MoveToBack(e3) // should be no-op
checkListPointers(t, l, []*Node[int]{e1, e4, e3})
e2 = l.InsertBefore(2, e1) // insert before front
checkListPointers(t, l, []*Node[int]{e2, e1, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e4) // insert before middle
checkListPointers(t, l, []*Node[int]{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e3) // insert before back
checkListPointers(t, l, []*Node[int]{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e1) // insert after front
checkListPointers(t, l, []*Node[int]{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e4) // insert after middle
checkListPointers(t, l, []*Node[int]{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e3) // insert after back
checkListPointers(t, l, []*Node[int]{e1, e4, e3, e2})
l.Remove(e2)
// Check standard iteration.
sum := 0
for e := l.Front(); e != nil; e = e.Next() {
sum += e.Value
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
// Clear all elements by iterating
var next *Node[int]
for e := l.Front(); e != nil; e = next {
next = e.Next()
l.Remove(e)
}
checkListPointers(t, l, []*Node[int]{})
}
func checkList[V comparable](t *testing.T, l *List[V], es []V) {
if !checkListLen(t, l, len(es)) {
return
}
i := 0
for e := l.Front(); e != nil; e = e.Next() {
le := e.Value
if le != es[i] {
t.Errorf("elt[%d].Value = %v, want %v", i, le, es[i])
}
i++
}
}
func TestExtending(t *testing.T) {
l1 := NewList[int]()
l2 := NewList[int]()
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l2.PushBack(4)
l2.PushBack(5)
l3 := NewList[int]()
l3.PushBackList(l1)
checkList(t, l3, []int{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []int{1, 2, 3, 4, 5})
l3 = NewList[int]()
l3.PushFrontList(l2)
checkList(t, l3, []int{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []int{1, 2, 3, 4, 5})
checkList(t, l1, []int{1, 2, 3})
checkList(t, l2, []int{4, 5})
l3 = NewList[int]()
l3.PushBackList(l1)
checkList(t, l3, []int{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []int{1, 2, 3, 1, 2, 3})
l3 = NewList[int]()
l3.PushFrontList(l1)
checkList(t, l3, []int{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []int{1, 2, 3, 1, 2, 3})
l3 = NewList[int]()
l1.PushBackList(l3)
checkList(t, l1, []int{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []int{1, 2, 3})
}
func TestRemove(t *testing.T) {
l := NewList[int]()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
checkListPointers(t, l, []*Node[int]{e1, e2})
e := l.Front()
l.Remove(e)
checkListPointers(t, l, []*Node[int]{e2})
l.Remove(e)
checkListPointers(t, l, []*Node[int]{e2})
}
func TestMove(t *testing.T) {
l := NewList[int]()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Node[int]{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Node[int]{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Node[int]{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Node[int]{e1, e2, e3, e4})
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Node[int]{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Node[int]{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Node[int]{e1, e4, e2, e3})
e2, e3, e4 = e4, e2, e3
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Node[int]{e1, e3, e2, e4})
}
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
func TestZeroList(t *testing.T) {
var l1 = new(List[int])
l1.PushFront(1)
checkList(t, l1, []int{1})
var l2 = new(List[int])
l2.PushBack(1)
checkList(t, l2, []int{1})
var l3 = new(List[int])
l3.PushFrontList(l1)
checkList(t, l3, []int{1})
var l4 = new(List[int])
l4.PushBackList(l2)
checkList(t, l4, []int{1})
}
// Test that a list l is not modified when calling InsertBefore with a mark
// that is not an element of l.
func TestInsertBeforeUnknownMark(t *testing.T) {
var l List[int]
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(1, new(Node[int]))
checkList(t, &l, []int{1, 2, 3})
}
// Test that a list l is not modified when calling InsertAfter with a mark that
// is not an element of l.
func TestInsertAfterUnknownMark(t *testing.T) {
var l List[int]
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(1, new(Node[int]))
checkList(t, &l, []int{1, 2, 3})
}
// Test that a list l is not modified when calling MoveAfter or MoveBefore with
// a mark that is not an element of l.
func TestMoveUnknownMark(t *testing.T) {
var l1 List[int]
e1 := l1.PushBack(1)
var l2 List[int]
e2 := l2.PushBack(2)
l1.MoveAfter(e1, e2)
checkList(t, &l1, []int{1})
checkList(t, &l2, []int{2})
l1.MoveBefore(e1, e2)
checkList(t, &l1, []int{1})
checkList(t, &l2, []int{2})
}