From 743ba437ef8dd446cbb5b82dcb9805e681a64aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Sat, 8 Jan 2022 18:01:09 +0100 Subject: [PATCH 1/9] New circular Seq operations: rotateRight, rotateLeft, startAt --- .../collection/decorators/SeqDecorator.scala | 59 +++++++++++++++++++ .../decorators/SeqDecoratorTest.scala | 18 ++++++ 2 files changed, 77 insertions(+) diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index 5ba8080..bc94bbc 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -55,4 +55,63 @@ 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) + + /** Considers the sequence circular and rotates it right by `step` places. + * + * @param step the number of places to rotate 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).rotateRight(1) => List(2, 3, 4, 1) + * }}} + */ + def rotateRight[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = + if (seq(coll).isEmpty) + bf.fromSpecific(coll)(new collection.View.Map(seq(coll), (a: seq.A) => a)) + 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 rotate 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).rotateLeft(1) => List(4, 1, 2, 3) + * }}} + */ + def rotateLeft[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = + rotateRight(-step) + + /** Considers the sequence circular and move its head to the `i` circular index. + * + * @param i the circular index of the element at the start of the new collection + * @tparam B the element type of the returned collection + * @return a new collection consisting of all elements of this collection + * circularly rotated so that the head element is at circular index `i`. + * @example {{{ + * List(1, 2, 3, 4).startAt(-1) => List(2, 3, 4, 1) + * }}} + */ + def startAt[B >: seq.A, That](i: IndexO)(implicit bf: BuildFrom[C, B, That]): That = + rotateLeft(i) + } diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index de7f1d2..068ea69 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -45,4 +45,22 @@ class SeqDecoratorTest { assertEquals(s.replaced(3, 4), Seq(1, 2, 4, 2, 1)) assertEquals(s.replaced(4, 4), s) } + + @Test def testRotatedRight(): Unit = { + val s = Seq(1, 2, 3, 2, 1) + val string = "RING" + assertEquals(s.rotateRight(1), Seq(1, 1, 2, 3, 2)) + assertEquals(string.rotateRight(1).mkString, "GRIN") + } + + @Test def testRotatedLeft(): Unit = { + val s = Seq(1, 2, 3, 2, 1) + assertEquals(s.rotateLeft(1), Seq(2, 3, 2, 1, 1)) + } + + @Test def testStartedAt(): Unit = { + val s = Seq(1, 2, 3, 2, 1) + assertEquals(s.startAt(1), Seq(2, 3, 2, 1, 1)) + } + } From 1006d1ebcd3f12bf6883d21fb3cdc56bc3a70315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Sat, 8 Jan 2022 21:29:45 +0100 Subject: [PATCH 2/9] Improved methods descriptions --- .../collection/decorators/SeqDecorator.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index bc94bbc..e0d9233 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -71,12 +71,12 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { /** Considers the sequence circular and rotates it right by `step` places. * - * @param step the number of places to rotate to the right + * @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).rotateRight(1) => List(2, 3, 4, 1) + * List(1, 2, 3, 4, 5).rotateRight(1) => List(2, 3, 4, 5, 1) * }}} */ def rotateRight[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = @@ -90,25 +90,25 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { /** Considers the sequence circular and rotates it left by `step` places. * - * @param step the number of places to rotate to the left + * @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).rotateLeft(1) => List(4, 1, 2, 3) + * List(1, 2, 3, 4, 5).rotateLeft(1) => List(4, 5, 1, 2, 3) * }}} */ def rotateLeft[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = rotateRight(-step) - /** Considers the sequence circular and move its head to the `i` circular index. + /** Considers the sequence circular and rotates it to start with the element at `i` circular index. * - * @param i the circular index of the element at the start of the new collection + * @param i the circular index of the element to be rotated at the start of the new collection * @tparam B the element type of the returned collection * @return a new collection consisting of all elements of this collection - * circularly rotated so that the head element is at circular index `i`. + * circularly rotated so to start with the element at circular index `i`. * @example {{{ - * List(1, 2, 3, 4).startAt(-1) => List(2, 3, 4, 1) + * List(1, 2, 3, 4, 5).startAt(2) => List(3, 4, 5, 1, 2) * }}} */ def startAt[B >: seq.A, That](i: IndexO)(implicit bf: BuildFrom[C, B, That]): That = From ba5d9004838bbb5adb42f592afd1c30903853957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Sun, 9 Jan 2022 17:54:27 +0100 Subject: [PATCH 3/9] Failing test on rotating empty Seq --- .../scala/scala/collection/decorators/SeqDecorator.scala | 5 ++--- .../scala/scala/collection/decorators/SeqDecoratorTest.scala | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index e0d9233..2b2fdd5 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -81,11 +81,10 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { */ def rotateRight[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = if (seq(coll).isEmpty) - bf.fromSpecific(coll)(new collection.View.Map(seq(coll), (a: seq.A) => a)) + 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)) + 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. diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index 068ea69..7ff3791 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -51,6 +51,7 @@ class SeqDecoratorTest { val string = "RING" assertEquals(s.rotateRight(1), Seq(1, 1, 2, 3, 2)) assertEquals(string.rotateRight(1).mkString, "GRIN") + assertEquals(Vector.empty.rotateRight(1), Vector.empty) } @Test def testRotatedLeft(): Unit = { From d123653fbbf5c3e6d7b8ae71782322fdf020e683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Sun, 9 Jan 2022 18:02:06 +0100 Subject: [PATCH 4/9] Clearer failing test --- .../scala/scala/collection/decorators/SeqDecoratorTest.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index 7ff3791..674cc5b 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -49,9 +49,10 @@ class SeqDecoratorTest { @Test def testRotatedRight(): Unit = { val s = Seq(1, 2, 3, 2, 1) val string = "RING" + val empty = Vector() assertEquals(s.rotateRight(1), Seq(1, 1, 2, 3, 2)) assertEquals(string.rotateRight(1).mkString, "GRIN") - assertEquals(Vector.empty.rotateRight(1), Vector.empty) + assertEquals(empty.rotateRight(1), empty) } @Test def testRotatedLeft(): Unit = { From 7ad2695e1191287df16b2e2cc12895000aa4c4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Thu, 13 Jan 2022 17:21:49 +0100 Subject: [PATCH 5/9] Failing test now ok --- .../scala/scala/collection/decorators/SeqDecoratorTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index 674cc5b..eafa66d 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -49,7 +49,7 @@ class SeqDecoratorTest { @Test def testRotatedRight(): Unit = { val s = Seq(1, 2, 3, 2, 1) val string = "RING" - val empty = Vector() + val empty = Vector.empty[Int] assertEquals(s.rotateRight(1), Seq(1, 1, 2, 3, 2)) assertEquals(string.rotateRight(1).mkString, "GRIN") assertEquals(empty.rotateRight(1), empty) From ebab8073bbbee896e6ffd5240d5e80a594b041b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Fri, 14 Jan 2022 11:05:36 +0100 Subject: [PATCH 6/9] Improved tests --- .../decorators/SeqDecoratorTest.scala | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index eafa66d..05a61a9 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -48,21 +48,30 @@ class SeqDecoratorTest { @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" - val empty = Vector.empty[Int] - assertEquals(s.rotateRight(1), Seq(1, 1, 2, 3, 2)) assertEquals(string.rotateRight(1).mkString, "GRIN") + val empty = Vector.empty[Int] assertEquals(empty.rotateRight(1), empty) } @Test def testRotatedLeft(): Unit = { val s = Seq(1, 2, 3, 2, 1) - assertEquals(s.rotateLeft(1), Seq(2, 3, 2, 1, 1)) + val sRotated = Seq(2, 3, 2, 1, 1) + assertEquals(s.rotateLeft(1), sRotated) + assertEquals(s.rotateLeft(6), sRotated) + assertEquals(s.rotateLeft(-4), sRotated) } @Test def testStartedAt(): Unit = { val s = Seq(1, 2, 3, 2, 1) - assertEquals(s.startAt(1), Seq(2, 3, 2, 1, 1)) + val sRotated = Seq(2, 3, 2, 1, 1) + assertEquals(s.startAt(1), sRotated) + assertEquals(s.startAt(6), sRotated) + assertEquals(s.startAt(-4), sRotated) } } From 3362ce5d64ad522bee9240933f41d58d8414bb3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Thu, 20 Jan 2022 08:40:29 +0100 Subject: [PATCH 7/9] Correct examples in descriptions --- src/main/scala/scala/collection/decorators/SeqDecorator.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index 2b2fdd5..63f1c1f 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -76,7 +76,7 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { * @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(2, 3, 4, 5, 1) + * 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 = @@ -94,7 +94,7 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { * @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(4, 5, 1, 2, 3) + * 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 = From 3d198ba910b6590646ddda06ead1e8125322e829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Thu, 7 Apr 2022 12:04:04 +0200 Subject: [PATCH 8/9] Improved test on String --- .../scala/scala/collection/decorators/SeqDecoratorTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index 05a61a9..58459ae 100644 --- a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala +++ b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala @@ -53,7 +53,7 @@ class SeqDecoratorTest { assertEquals(s.rotateRight(6), sRotated) assertEquals(s.rotateRight(-4), sRotated) val string = "RING" - assertEquals(string.rotateRight(1).mkString, "GRIN") + assertEquals(string.rotateRight(1), "GRIN") val empty = Vector.empty[Int] assertEquals(empty.rotateRight(1), empty) } From 3577f3cf3746ddc591cc4ce86101537efc337763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20C=C3=A0llisto?= Date: Sat, 25 May 2024 15:33:52 +0200 Subject: [PATCH 9/9] Deleted method rotateAt and added applyO --- .../collection/decorators/SeqDecorator.scala | 25 +++++++++---------- .../decorators/SeqDecoratorTest.scala | 17 ++++++------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/main/scala/scala/collection/decorators/SeqDecorator.scala b/src/main/scala/scala/collection/decorators/SeqDecorator.scala index 63f1c1f..02ddfff 100644 --- a/src/main/scala/scala/collection/decorators/SeqDecorator.scala +++ b/src/main/scala/scala/collection/decorators/SeqDecorator.scala @@ -69,6 +69,18 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { 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 @@ -100,17 +112,4 @@ class SeqDecorator[C, S <: IsSeq[C]](coll: C)(implicit val seq: S) { def rotateLeft[B >: seq.A, That](step: Int)(implicit bf: BuildFrom[C, B, That]): That = rotateRight(-step) - /** Considers the sequence circular and rotates it to start with the element at `i` circular index. - * - * @param i the circular index of the element to be rotated at the start of the new collection - * @tparam B the element type of the returned collection - * @return a new collection consisting of all elements of this collection - * circularly rotated so to start with the element at circular index `i`. - * @example {{{ - * List(1, 2, 3, 4, 5).startAt(2) => List(3, 4, 5, 1, 2) - * }}} - */ - def startAt[B >: seq.A, That](i: IndexO)(implicit bf: BuildFrom[C, B, That]): That = - rotateLeft(i) - } diff --git a/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala b/src/test/scala/scala/collection/decorators/SeqDecoratorTest.scala index 58459ae..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._ @@ -46,6 +46,13 @@ class SeqDecoratorTest { 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) @@ -66,12 +73,4 @@ class SeqDecoratorTest { assertEquals(s.rotateLeft(-4), sRotated) } - @Test def testStartedAt(): Unit = { - val s = Seq(1, 2, 3, 2, 1) - val sRotated = Seq(2, 3, 2, 1, 1) - assertEquals(s.startAt(1), sRotated) - assertEquals(s.startAt(6), sRotated) - assertEquals(s.startAt(-4), sRotated) - } - }