11package de.ronny_h.aoc.extensions.collections
22
3- import java.util.*
3+ import kotlin.math.abs
44
55class MutableRingList <T >() {
6- private val list: MutableList <T > = LinkedList <T >()
7- private var offset = 0
6+ private data class Node <T >(var value : T ) {
7+ lateinit var prev: Node <T >
8+ lateinit var next: Node <T >
9+ }
10+
11+ private lateinit var head: Node <T >
12+ private var size = 0
813
914 constructor (initialList: List <T >) : this () {
10- list.addAll(initialList)
15+ initialList.forEach { add(it) }
1116 }
1217
1318 constructor (size: Int , init : (index: Int ) -> T ) : this () {
14- repeat(size) { index -> list. add(init (index)) }
19+ repeat(size) { index -> add(init (index)) }
1520 }
1621
1722 companion object {
1823 fun <T > mutableRingListOf (vararg elements : T ): MutableRingList <T > = MutableRingList (elements.toList())
1924 fun mutableRingListOf (elements : String ): MutableRingList <Char > = MutableRingList (elements.toList())
2025 }
2126
22- operator fun get (index : Int ) = list[ (index + offset) % list.size]
27+ operator fun get (index : Int ): T = head.goSteps (index).value
2328
2429 operator fun set (index : Int , value : T ) {
25- list[ (index + offset) % list.size] = value
30+ head.goSteps (index).value = value
2631 }
2732
2833 /* *
2934 * Inserts the given [value] at the current position (i.e. the current start of the ring list).
3035 */
3136 fun insert (value : T ) {
32- list.add(offset, value)
37+ head = addAsLast(value)
38+ }
39+
40+ /* *
41+ * Adds the given [value] to the end of this ring list (i.e. inserts it before the current position).
42+ */
43+ fun add (value : T ) {
44+ addAsLast(value)
3345 }
3446
3547 /* *
3648 * Removes the value at the current position (i.e. the current start of the ring list) and returns it.
3749 * All elements after the current position are shifted 1 position left.
3850 *
3951 * @return The element that has been removed.
52+ * @throws IndexOutOfBoundsException If this ring list is empty.
4053 */
41- fun removeFirst (): T = list.removeAt(offset)
54+ fun removeFirst (): T {
55+ if (size == 0 ) {
56+ throw IndexOutOfBoundsException (" Unable to remove an element from an empty list." )
57+ }
58+ size--
59+ val node = head.next
60+ val removed = head.value
61+ node.prev = head.prev
62+ node.prev.next = node
63+ head = node
64+ return removed
65+ }
4266
4367 /* *
4468 * Makes [n] elements move from the end to the front, but maintain their order otherwise.
4569 */
4670 fun shiftRight (n : Int ): MutableRingList <T > {
47- offset = (offset + list.size - n).mod(list.size )
71+ head = head.goSteps( - n )
4872 return this
4973 }
5074
5175 /* *
5276 * Makes [n] elements move from the front to the end, but maintain their order otherwise.
5377 */
5478 fun shiftLeft (n : Int ): MutableRingList <T > {
55- offset = (offset + list.size + n).mod(list.size )
79+ head = head.goSteps(n )
5680 return this
5781 }
5882
5983 /* *
6084 * Swaps the elements at [indexA] and [indexB].
6185 */
6286 fun swap (indexA : Int , indexB : Int ): MutableRingList <T > {
63- val tmp = get(indexA)
64- set(indexA, get(indexB))
65- set(indexB, tmp)
87+ swapValues(head.goSteps(indexA), head.goSteps(indexB))
6688 return this
6789 }
6890
@@ -71,35 +93,93 @@ class MutableRingList<T>() {
7193 * If not unique, only their first occurrences are swapped.
7294 */
7395 fun swap (elemA : T , elemB : T ): MutableRingList <T > {
74- val indexA = list.indexOf(elemA)
75- val indexB = list.indexOf(elemB)
76- require(indexA >= 0 ) { " $elemA is not in the list" }
77- require(indexB >= 0 ) { " $elemB is not in the list" }
78- list[indexA] = elemB
79- list[indexB] = elemA
96+ swapValues(findNode(elemA), findNode(elemB))
8097 return this
8198 }
8299
83- fun reverseSubList (s : Int , length : Int ): MutableRingList <T > {
84- val start = (offset + s) % list.size
85- if (start + length < list.size) {
86- list.subList(start, start + length).toList()
87- } else {
88- list.subList(start, list.size) + list.subList(0 , length - list.size + start)
100+ /* *
101+ * Reverses the elements of the sublist starting at [startIndex] (inclusive) with length [length].
102+ */
103+ fun reverseSubList (startIndex : Int , length : Int ): MutableRingList <T > {
104+ var first = head.goSteps(startIndex)
105+ var last = first.goSteps(length - 1 )
106+ var swaps = length / 2
107+ while (swaps > 0 ) {
108+ swapValues(first, last)
109+ first = first.next
110+ last = last.prev
111+ swaps--
89112 }
90- .reversed()
91- .forEachIndexed { i, item ->
92- list[(start + i) % list.size] = item
93- }
94113 return this
95114 }
96115
97116 /* *
98117 * @return A snapshot of the current state of this [MutableRingList]
99118 */
100- fun toList (): List <T > = list.subList(offset, list.size) + list.subList(0 , offset)
119+ fun toList (): List <T > {
120+ val list = ArrayList <T >(size)
121+ var node = head
122+ repeat(size) {
123+ list.add(node.value)
124+ node = node.next
125+ }
126+ return list
127+ }
101128
102129 override fun toString (): String = toList().toString()
103130
104131 fun toJoinedString (): String = toList().joinToString(" " )
132+
133+ private fun Node<T>.goSteps (n : Int ): Node <T > {
134+ var node = this
135+ repeat(abs(n) % size) {
136+ node = (if (n > 0 ) node.next else node.prev)
137+ }
138+ return node
139+ }
140+
141+ private fun addAsLast (value : T ): Node <T > {
142+ val newNode = Node (value)
143+ size++
144+ if (this ::head.isInitialized) {
145+ insertBefore(head, newNode)
146+ } else {
147+ initHead(newNode)
148+ }
149+ return newNode
150+ }
151+
152+ private fun insertBefore (
153+ current : Node <T >,
154+ newNode : Node <T >
155+ ) {
156+ val prev = current.prev
157+ current.prev = newNode
158+ newNode.next = current
159+ newNode.prev = prev
160+ prev.next = newNode
161+ }
162+
163+ private fun initHead (newNode : Node <T >) {
164+ newNode.prev = newNode
165+ newNode.next = newNode
166+ head = newNode
167+ }
168+
169+ private fun swapValues (nodeA : Node <T >, nodeB : Node <T >) {
170+ val tmp = nodeA.value
171+ nodeA.value = nodeB.value
172+ nodeB.value = tmp
173+ }
174+
175+ private fun findNode (value : T ): Node <T > {
176+ var node = head
177+ while (node.value != value) {
178+ node = node.next
179+ require(node != = head) {
180+ " $value is not in the list"
181+ }
182+ }
183+ return node
184+ }
105185}
0 commit comments