diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index 5ba8080..02ddfff 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -55,4 +55,61 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { */ def replaced[B >: seq.A, That](elem: B, replacement: B)(implicit bf: BuildFrom[C, B, That]): That = bf.fromSpecific(coll)(new collection.View.Map(seq(coll), (a: seq.A) => if (a == elem) replacement else a)) + + /** + * for improved readability, the integer index of an element in a Seq + */ + type Index = Int + + /** + * the integer index of an element in a circular Seq, any value is valid + */ + type IndexO = Int + + private def index(i: IndexO): Index = + java.lang.Math.floorMod(i, seq(coll).size) + + /** Gets the element at some circular index. + * + * @param i circular index + * @return the element of the sequence at the given index + * @throws java.lang.ArithmeticException if the sequence is empty + * @example {{{ + * Seq(0, 1, 2).applyO(3) => 0 + * }}} + */ + def applyO(i: IndexO): this.seq.A = + seq(coll).apply(index(i)) + + /** Considers the sequence circular and rotates it right by `step` places. + * + * @param step the number of places to be rotated to the right + * @tparam B the element type of the returned collection + * @return a new collection consisting of all elements of this collection + * circularly rotated by `step` places to the right. + * @example {{{ + * List(1, 2, 3, 4, 5).rotateRight(1) => List(5, 1, 2, 3, 4) + * }}} + */ + def rotateRight[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = + if (seq(coll).isEmpty) + bf.fromSpecific(coll)(collection.View.Empty) + else { + val j: Index = seq(coll).size - index(step) + bf.fromSpecific(coll)(new collection.View.Drop(seq(coll), j) ++ new collection.View.Take(seq(coll), j)) + } + + /** Considers the sequence circular and rotates it left by `step` places. + * + * @param step the number of places to be rotated to the left + * @tparam B the element type of the returned collection + * @return a new collection consisting of all elements of this collection + * circularly rotated by `step` places to the left. + * @example {{{ + * List(1, 2, 3, 4, 5).rotateLeft(1) => List(2, 3, 4, 5, 1) + * }}} + */ + def rotateLeft[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = + rotateRight(-step) + } diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index de7f1d2..b751825 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -1,7 +1,7 @@ package scala.collection package decorators -import org.junit.Assert.assertEquals +import org.junit.Assert.{assertEquals, assertSame, assertThrows} import org.junit.Test import scala.collection.immutable._ @@ -45,4 +45,32 @@ class SeqDecoratorTest { assertEquals(s.replaced(3, 4), Seq(1, 2, 4, 2, 1)) assertEquals(s.replaced(4, 4), s) } + + @Test def testApplyO(): Unit = { + val s = Seq(0, 1, 2) + assertEquals(s.applyO(3), 0) + val empty = Vector.empty[Int] + assertThrows(classOf[java.lang.ArithmeticException], () => empty.applyO(1)) + } + + @Test def testRotatedRight(): Unit = { + val s = Seq(1, 2, 3, 2, 1) + val sRotated = Seq(1, 1, 2, 3, 2) + assertEquals(s.rotateRight(1), sRotated) + assertEquals(s.rotateRight(6), sRotated) + assertEquals(s.rotateRight(-4), sRotated) + val string = "RING" + assertEquals(string.rotateRight(1), "GRIN") + val empty = Vector.empty[Int] + assertEquals(empty.rotateRight(1), empty) + } + + @Test def testRotatedLeft(): Unit = { + val s = Seq(1, 2, 3, 2, 1) + val sRotated = Seq(2, 3, 2, 1, 1) + assertEquals(s.rotateLeft(1), sRotated) + assertEquals(s.rotateLeft(6), sRotated) + assertEquals(s.rotateLeft(-4), sRotated) + } + }