Skip to content

Commit

Permalink
Slice
Browse files Browse the repository at this point in the history
  • Loading branch information
skozlov committed Aug 2, 2024
1 parent 4afb935 commit 72dd3f4
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 2 deletions.
53 changes: 53 additions & 0 deletions src/main/scala/com/github/skozlov/commons/collection/Slice.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.github.skozlov.commons.collection

import scala.collection.IndexedSeqView.SomeIndexedSeqOps
import scala.collection.{IndexedSeqView, SeqOps}

class Slice[+A](val underlying: SomeIndexedSeqOps[A], val from: Int, val until: Int) extends IndexedSeqView[A] {
import Slice.*

checkBounds(underlying, from, until)

override val length: Int = until - from

@throws[IndexOutOfBoundsException]
protected def indexToUnderlying(i: Int): Int = {
if (i < 0 || i >= length) {
throw IndexOutOfBoundsException(s"$i is out of bounds (min 0, max ${length-1})")
}
from + i
}

override def apply(i: Int): A = underlying(indexToUnderlying(i))

override def slice(from: Int, until: Int): Slice[A] = {
if (from == 0 && until == this.length) {
this
}
else {
checkBounds(this, from, until)
Slice(underlying, this.from + from, this.from + until)
}
}

override def take(n: Int): Slice[A] = slice(0, math.min(math.max(n, 0), length))

override def drop(n: Int): Slice[A] = slice(math.min(math.max(n, 0), length), length)

override def takeRight(n: Int): Slice[A] = drop(length - math.max(n, 0))

override def dropRight(n: Int): Slice[A] = take(length - math.max(n, 0))
}

object Slice {
@throws[IllegalArgumentException]
def checkBounds[A](underlying: SomeIndexedSeqOps[A], from: Int, until: Int): Unit = {
require(from >= 0, s"Negative `from`: $from")
require(from <= until, s"`from` ($from) is greater than `until` ($until)")
require(until <= underlying.length, s"`until` ($until) is greater than underlying length (${underlying.length})")
}

type AnyConstr[X] = Any

type SomeSeqOps[+A] = SeqOps[A, AnyConstr, _]
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.skozlov.algorithms
package com.github.skozlov

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.github.skozlov.algorithms.sort

import com.github.skozlov.algorithms.Test
import com.github.skozlov.Test

class SortTest extends Test {
private val cases: Seq[Seq[(Int, Int)]] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.github.skozlov.commons.collection

import com.github.skozlov.Test

class SliceTest extends Test {
test("constructor") {
val underlying = Array(1, 2, 3, 4, 5)
Slice(underlying, from = 1, until = 4).toSeq shouldBe Seq(2, 3, 4)
Slice(underlying, from = 0, until = 2).toSeq shouldBe Seq(1, 2)
Slice(underlying, from = 3, until = 5).toSeq shouldBe Seq(4, 5)
Slice(underlying, from = 1, until = 1).toSeq shouldBe Seq()
the[IllegalArgumentException] thrownBy Slice(underlying, from = -1, until = 4) should have message "requirement failed: Negative `from`: -1"
the[IllegalArgumentException] thrownBy Slice(underlying, from = 2, until = 1) should have message "requirement failed: `from` (2) is greater than `until` (1)"
the[IllegalArgumentException] thrownBy Slice(underlying, from = 3, until = 6) should have message "requirement failed: `until` (6) is greater than underlying length (5)"
}

test("changing underlying") {
val underlying = Array(1, 2, 3, 4, 5)
val slice = Slice(underlying, from = 1, until = 4)
slice.toSeq shouldBe Seq(2, 3, 4)
for (i <- underlying.indices) {
underlying(i) = underlying(i) * 10
}
slice.toSeq shouldBe Seq(20, 30, 40)
}

test("length"){
val underlying = Array(1, 2, 3, 4, 5)
Slice(underlying, from = 1, until = 4).length shouldBe 3
Slice(underlying, from = 1, until = 2).length shouldBe 1
Slice(underlying, from = 1, until = 1).length shouldBe 0
}

test("apply(i: Int)"){
val slice = Slice(underlying = Array(1, 2, 3, 4), from = 1, until = 3)
slice(0) shouldBe 2
slice(1) shouldBe 3
the[IndexOutOfBoundsException] thrownBy slice(2) should have message "2 is out of bounds (min 0, max 1)"
the[IndexOutOfBoundsException] thrownBy slice(-1) should have message "-1 is out of bounds (min 0, max 1)"
}

test("slice(from: Int, until: Int)") {
val underlying = Array(1, 2, 3, 4, 5, 6, 7)
val slice = Slice(underlying, from = 1, until = 6)
slice.slice(from = 1, until = 4).toSeq shouldBe Seq(3, 4, 5)
slice.slice(from = 0, until = 5).toSeq shouldBe Seq(2, 3, 4, 5, 6)
slice.slice(from = 1, until = 1).toSeq shouldBe Seq()
the[IllegalArgumentException] thrownBy slice.slice(from = -1, until = 5) should have message "requirement failed: Negative `from`: -1"
the[IllegalArgumentException] thrownBy slice.slice(from = 1, until = 0) should have message "requirement failed: `from` (1) is greater than `until` (0)"
the[IllegalArgumentException] thrownBy slice.slice(from = 0, until = 6) should have message "requirement failed: `until` (6) is greater than underlying length (5)"
}

test("take(n: Int)") {
val slice = Slice(underlying = Array(1, 2, 3, 4), from = 1, until = 3)
(slice take 0).toSeq shouldBe Seq()
(slice take -1).toSeq shouldBe Seq()
(slice take 1).toSeq shouldBe Seq(2)
(slice take 2).toSeq shouldBe Seq(2, 3)
(slice take 3).toSeq shouldBe Seq(2, 3)
}

test("drop(n: Int)") {
val slice = Slice(underlying = Array(1, 2, 3, 4), from = 1, until = 3)
(slice drop 0).toSeq shouldBe Seq(2, 3)
(slice drop -1).toSeq shouldBe Seq(2, 3)
(slice drop 1).toSeq shouldBe Seq(3)
(slice drop 2).toSeq shouldBe Seq()
(slice drop 3).toSeq shouldBe Seq()
}

test("takeRight(n: Int)") {
val slice = Slice(underlying = Array(1, 2, 3, 4), from = 1, until = 3)
(slice takeRight 0).toSeq shouldBe Seq()
(slice takeRight -1).toSeq shouldBe Seq()
(slice takeRight 1).toSeq shouldBe Seq(3)
(slice takeRight 2).toSeq shouldBe Seq(2, 3)
(slice takeRight 3).toSeq shouldBe Seq(2, 3)
}

test("dropRight(n: Int)") {
val slice = Slice(underlying = Array(1, 2, 3, 4), from = 1, until = 3)
(slice dropRight 0).toSeq shouldBe Seq(2, 3)
(slice dropRight -1).toSeq shouldBe Seq(2, 3)
(slice dropRight 1).toSeq shouldBe Seq(2)
(slice dropRight 2).toSeq shouldBe Seq()
(slice dropRight 3).toSeq shouldBe Seq()
}
}

0 comments on commit 72dd3f4

Please sign in to comment.