[ all classes ]
[ lzwpack.data ]
Coverage Summary for Class: ListVector (lzwpack.data)
| Class | Method, % | Line, % |
|---|---|---|
| ListVector | 90% (27/ 30) | 90.6% (29/ 32) |
| ListVector$ | 100% (8/ 8) | 100% (11/ 11) |
| ListVector$$anon$1 | 100% (4/ 4) | 100% (6/ 6) |
| ListVector$mcB$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcC$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcD$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcF$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcI$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcJ$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcS$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcV$sp | 0% (0/ 22) | 0% (0/ 23) |
| ListVector$mcZ$sp | 0% (0/ 22) | 0% (0/ 23) |
| total | 16.2% (39/ 240) | 18% (46/ 256) |
1 package lzwpack.data
2
3 import ListVector._
4 import cats._
5 import cats.implicits._
6
7
8 /**
9 * A [[ListVector]] is a list-like data structure in the sense that it provides
10 * efficient random access and append; prepend is not yet supported.
11 * Compared to a [[SparseVector]] this data structure is _bounded_ meaning that given a list with size |L| > 0
12 * there is guaranteed to be an element in each k ∈ [0..|L| - 1]. Updates to indices outside of the index range are prohibited.
13 */
14 class ListVector[@specialized A] private[data](private[data] val trie: SparseVector[A], val size: Int, offset: Int = 0) {
15 /**
16 * Returns a new [[ListVector]] for which f(a) == true for all elements.
17 * Note that indices are not preserved; for instance, if index 3 was filtered out and 4 was not,
18 * then index 4 would become index 3. This is to preserve list-like semantics.
19 */
20 def filter(f: A => Boolean): ListVector[A] = foldLeft(empty[A])((as, a) => if (f(a)) as + a else as)
21
22 /**
23 * Concatenates the argument to this [[ListVector]].
24 */
25 def concat(as1: ListVector[A]): ListVector[A] = as1.foldLeft(this)(_ + _)
26
27 /**
28 * Returns the first element `a` in this [[ListVector]] for which `f(a)` returns true, wrapped in an [[scala.Option]].
29 * If no element matches `f`, then a [[scala.None]] is returned.
30 */
31 def find(f: A => Boolean): Option[A] = foldRight(none[A]) { (a, rest) => if (f(a)) a.some else rest }
32
33 /**
34 * Unsafe version of [[get]].
35 * @throws scala.IndexOutOfBoundsException if this [[ListVector]] does not contain the given index
36 */
37 def apply(i: Int): A = get(i).getOrElse(throw new IndexOutOfBoundsException(s"$i > $size (offset $offset)"))
38
39 /**
40 * Returns the first element of this [[ListVector]].
41 * @throws scala.IndexOutOfBoundsException if this [[ListVector]] is empty
42 */
43 lazy val head: A = this(0)
44
45 /**
46 * Like [[head]], but returns an [[scala.Option]] determining the success of the operation instead of throwing
47 * an exception.
48 */
49 lazy val headOption: Option[A] = get(0)
50
51 /**
52 * Returns a [[ListVector]] with all the elements from this vector except the first one (see [[head]]).
53 * The first index is dropped so that
54 * {{{
55 * ListVector(1,2,3).tail.get(0) == Some(2)
56 * }}}
57 */
58 lazy val tail: ListVector[A] = drop(1)
59
60 /**
61 * Returns a new [[ListVector]] without the first `n` items.
62 */
63 def drop(n: Int): ListVector[A] = {
64 if (n > size) empty[A] else new ListVector(trie, size - n, offset + n)
65 }
66
67 /**
68 * Returns a new [[ListVector]] with only the first `n` items present.
69 */
70 def take(n: Int): ListVector[A] = new ListVector[A](trie, Math.min(n, size), offset)
71
72 /**
73 * Appends the given value to the end of this [[ListVector]].
74 * Complexity: O(log n)
75 */
76 def append(a: A) = new ListVector(trie.updated(offset + size, a), size + 1)
77
78 /**
79 * If there exists an element with the given index, returns an [[scala.Option]] containing the element or
80 * an empty option otherwise.
81 * Complexity: O(log n)
82 */
83 def get(i: Int): Option[A] = if (i < size) trie get (offset + i) else none[A]
84
85 /**
86 * Returns true if this [[ListVector]] contains an element at index `i`, `false` otherwise.
87 */
88 def contains(i: Int): Boolean = get(i).isDefined
89
90 /**
91 * Alias for [[append]].
92 */
93 def +(a: A): ListVector[A] = append(a)
94
95 /**
96 * Alias for [[append]].
97 */
98 def :+(a: A): ListVector[A] = append(a)
99
100 /**
101 * Returns `true` if this [[ListVector]] is empty (i.e. contains no elements), `false` otherwise.
102 */
103 def isEmpty: Boolean = size == 0
104
105 // Uses random access for speed (?)
106 @annotation.tailrec
107 private def foldl[B](start: Int, end: Int, init: B, f: (B, A) => B): B = {
108 if (start == end) init
109 else foldl(start + 1, end, f(init, this(start)), f)
110 }
111
112 private def foldr[B](start: Int, end: Int, init: => B, f: (A, => B) => B): B = {
113 if (start == end) init
114 else f(this(start), foldr(start + 1, end, init, f))
115 }
116
117 /**
118 * Left folds the elements of this [[ListVector]].
119 */
120 def foldLeft[B](init: B)(f: (B, A) => B): B = foldl(0, size, init, f)
121
122 /**
123 * Right folds the elements of this ListVector.
124 */
125 def foldRight[B](init: => B)(f: (A, => B) => B): B = foldr(0, size, init, f)
126
127 /**
128 * Returns an [[scala.Iterator]] for this [[ListVector]].
129 * @note we do not implement the Iterable trait in order to make our own data structure methods
130 */
131 def iterator: Iterator[A] = new Iterator[A] {
132 private var i = offset
133
134 override def hasNext: Boolean = ListVector.this.contains(i)
135
136 override def next(): A = {
137 val a = ListVector.this(i)
138 i += 1
139 a
140 }
141 }
142
143 /**
144 * Returns a new [[ListVector]] with each element applied to `f`.
145 */
146 def map[B](f: A => B): ListVector[B] =
147 foldLeft(empty[B])((acc, a) => acc + f(a))
148
149 override lazy val toString: String = s"ListVector(${this.map(_.toString).intercalate(", ")})"
150
151 /**
152 * Hash code calculated as in [[java.util.List]].
153 */
154 override lazy val hashCode: Int = foldLeft(1)((acc, e) => 31 * acc + e.hashCode())
155
156 /**
157 * Converts this ListVector to a [[scala.Seq]].
158 */
159 lazy val toSeq: Seq[A] = iterator.toSeq
160 }
161
162 object ListVector {
163 def empty[A] = new ListVector(SparseVector.empty[A], 0)
164
165 def apply[A](as: A*): ListVector[A] = seq(as)
166
167 def seq[A](as: Seq[A]): ListVector[A] = as.foldLeft(empty[A])(_ + _)
168
169 def unfold[A, S](init: S)(f: S => Option[(A, S)]): ListVector[A] = {
170 @annotation.tailrec
171 def iter(acc: ListVector[A], s1: S): ListVector[A] = f(s1) match {
172 case None => acc
173 case Some((a, s2)) => iter(acc + a, s2)
174 }
175 iter(empty[A], init)
176 }
177
178 def range[A: Integral](from: A, to: A)(implicit integral: Integral[A]): ListVector[A] = {
179 import integral._
180 // We need to append the upper bound explicitly in order to avoid numeric overflows when comparing values.
181 unfold(from)(_.some.filter(_ < to).map(n => (n, n + one))) + to
182 }
183 }
184
185 object :: {
186 def unapply[A](as: ListVector[A]): Option[(A, ListVector[A])] = as.headOption.map((_, as.tail))
187 }