Sorted and Ordered Vector Type Support (#3310)

* Implemented SortedVec class

* Added support for ordered vectors

* Made SortedVec an abstract class and introduced an OrderedNonces case class
This commit is contained in:
Nadav Kohen 2021-06-25 08:41:10 -05:00 committed by GitHub
parent c72c5f84e3
commit 5685371e11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 150 additions and 0 deletions

View file

@ -0,0 +1,102 @@
package org.bitcoins.core.util
import org.bitcoins.core.util.sorted.SortedVec
import org.bitcoins.crypto.{ECPublicKey, NetworkElement, SchnorrNonce}
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
class SortedVecTest extends BitcoinSUnitTest {
behavior of "SortedVec"
implicit val networkElementOrd: Ordering[NetworkElement] = {
case (x: NetworkElement, y: NetworkElement) =>
x.bytes.compare(y.bytes)
}
it should "sort correctly on construction with normal orderings" in {
assert(SortedVec.sort(Vector(1, 2, 3)) == SortedVec(Vector(1, 2, 3)))
assert(SortedVec.sort(Vector(3, 2, 1)) == SortedVec(Vector(1, 2, 3)))
assert(
SortedVec.sort(Vector(3.0, 2.0, 1.123)) == SortedVec(
Vector(1.123, 2.0, 3.0)))
assert(
SortedVec.sort(Vector('c', 'b', 'a')) == SortedVec(Vector('a', 'b', 'c')))
assert(
SortedVec.sort(Vector("cab", "ceb", "abc")) == SortedVec(
Vector("abc", "cab", "ceb")))
assert(
SortedVec.sort[SchnorrNonce, NetworkElement](Vector(
SchnorrNonce(
"c4b89873c8753de3f0a9e94c4a6190badaa983513a6624a3469eb4577904bfea"),
SchnorrNonce(
"92efe81609c773d97da2b084eb691f48ef5e926acc6eecd629f80fb1184711bc")
)) == SortedVec[SchnorrNonce, NetworkElement](Vector(
SchnorrNonce(
"92efe81609c773d97da2b084eb691f48ef5e926acc6eecd629f80fb1184711bc"),
SchnorrNonce(
"c4b89873c8753de3f0a9e94c4a6190badaa983513a6624a3469eb4577904bfea")
)))
}
it should "fail to construct with unsorted vector under normal orderings" in {
assertThrows[IllegalArgumentException](SortedVec(Vector(3, 2, 1)))
assertThrows[IllegalArgumentException](SortedVec(Vector(3.0, 2.0, 1.123)))
assertThrows[IllegalArgumentException](SortedVec(Vector('c', 'b', 'a')))
assertThrows[IllegalArgumentException](
SortedVec(Vector("cab", "ceb", "abc")))
assertThrows[IllegalArgumentException](
SortedVec[SchnorrNonce, NetworkElement](Vector(
SchnorrNonce(
"c4b89873c8753de3f0a9e94c4a6190badaa983513a6624a3469eb4577904bfea"),
SchnorrNonce(
"92efe81609c773d97da2b084eb691f48ef5e926acc6eecd629f80fb1184711bc")
)))
}
it should "sort correctly on construction with custom orderings" in {
implicit val parityOrd: Ordering[Int] = { case (x: Int, y: Int) =>
(math.abs(x) % 2) - (math.abs(y) % 2)
}
assert(SortedVec.sort(Vector(1, 2, 3, 4)) == SortedVec(Vector(2, 4, 1, 3)))
assert(SortedVec.sort(Vector(2, 4, 1, 3)) == SortedVec(Vector(2, 4, 1, 3)))
implicit val remainderOrd: Ordering[Double] = {
case (x: Double, y: Double) =>
val diff = (x - x.floor) - (y - y.floor)
if (diff > 0) 1 else if (diff == 0) 0 else -1
}
assert(
SortedVec.sort(Vector(0.95, 123.123, 5.55)) == SortedVec(
Vector(123.123, 5.55, 0.95)))
}
it should "fail to construct with unsorted vector under custom orderings" in {
implicit val parityOrd: Ordering[Int] = { case (x: Int, y: Int) =>
(math.abs(x) % 2) - (math.abs(y) % 2)
}
assertThrows[IllegalArgumentException](SortedVec(Vector(1, 2, 3, 4)))
implicit val remainderOrd: Ordering[Double] = {
case (x: Double, y: Double) =>
val diff = (x - x.floor) - (y - y.floor)
if (diff > 0) 1 else if (diff == 0) 0 else -1
}
assertThrows[IllegalArgumentException](
SortedVec(Vector(0.95, 5.55, 123.123)))
}
it should "correctly handle ordered list" in {
val nonce1 = ECPublicKey.freshPublicKey.schnorrNonce
val nonce2 = ECPublicKey.freshPublicKey.schnorrNonce
val nonce3 = ECPublicKey.freshPublicKey.schnorrNonce
val nonces = Vector(nonce1, nonce2, nonce3)
val wrongOrder = Vector(nonce2, nonce3, nonce1)
val _ = SortedVec(nonces)(SortedVec.forOrdered(nonces))
assertThrows[IllegalArgumentException](
SortedVec(wrongOrder)(SortedVec.forOrdered(nonces)))
}
}

View file

@ -0,0 +1,8 @@
package org.bitcoins.core.util.sorted
import org.bitcoins.crypto.SchnorrNonce
/** Represents an ordered set of SchnorrNonces */
case class OrderedNonces(vec: Vector[SchnorrNonce])
extends SortedVec[SchnorrNonce, SchnorrNonce](vec,
SortedVec.forOrdered(vec))

View file

@ -0,0 +1,40 @@
package org.bitcoins.core.util.sorted
import org.bitcoins.core.util.SeqWrapper
/** Wraps a sorted Vector[T] and its Ordering (which may be for a supertype).
*
* For example SortedVec[SchnorrNonce, NetworkElement] would be a
* Vector[SchnorrNonce] sorted as NetworkElements.
*/
abstract class SortedVec[T, B >: T](
override val wrapped: Vector[T],
ord: Ordering[B])
extends SeqWrapper[T] {
require(
wrapped.init.zip(wrapped.tail).forall { case (x, y) => ord.lteq(x, y) },
s"Vector must be sorted. $wrapped")
}
object SortedVec {
/** For use with ordered lists so that changing order will not be allowed.
*/
def forOrdered[T](vec: Vector[T]): Ordering[T] = { case (x, y) =>
vec.indexOf(x) - vec.indexOf(y)
}
private case class SortedVecImpl[T, B >: T](vec: Vector[T])(implicit
val ord: Ordering[B])
extends SortedVec[T, B](vec, ord)
def sort[T, B >: T](vec: Vector[T])(implicit
ord: Ordering[B]): SortedVec[T, B] = {
SortedVecImpl(vec.sorted[B])
}
def apply[T, B >: T](vec: Vector[T])(implicit
ord: Ordering[B]): SortedVec[T, B] = {
SortedVecImpl(vec)
}
}