Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9861f150f6e04200b991de30969ea536)](https://app.codacy.com/manual/scalaprof/Comparer?utm_source=github.com&utm_medium=referral&utm_content=rchillyard/Comparer&utm_campaign=Badge_Grade_Settings)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.phasmidsoftware/comparer_2.13/badge.svg?color=blue)](https://maven-badges.herokuapp.com/maven-central/com.phasmidsoftware_2.13/comparer/)
[![CircleCI](https://circleci.com/gh/rchillyard/Comparer.svg?style=svg)](https://circleci.com/gh/rchillyard/Comparer)
# Comparer A functional three-way comparer.
# Comparer: a functional three-way comparer.

## Making _Comparer_ a dependency
If you're using sbt, then include the following line (see the badge above for the current version):
Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ organization := "com.phasmidsoftware"

name := "Comparer"

version := "1.0.9"
version := "1.0.10"

scalaVersion := "2.13.6"
scalaVersion := "2.13.16"

scalacOptions += "-deprecation"

val scalaTestVersion = "3.1.1"
val scalaTestVersion = "3.2.19"

resolvers += "Typesafe Repository" at "https://repo.typesafe.com/typesafe/releases/"

Expand Down
45 changes: 45 additions & 0 deletions src/it/scala/com/phasmidsoftware/sort/SortedFuncSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2024. Phasmid Software. Comparer: functional comparison library.
*/

package com.phasmidsoftware.sort

import org.scalatest.concurrent.PatienceConfiguration.Timeout
import org.scalatest.concurrent.{Futures, ScalaFutures}
import org.scalatest.time.{Seconds, Span}
import org.scalatest.{flatspec, matchers}

import scala.language.postfixOps
import scala.util.Random

/**
* @author scalaprof
*/
class SortedFuncSpec extends flatspec.AnyFlatSpec with matchers.should.Matchers with Futures with ScalaFutures {

behavior of "merge"

// TODO Find out why this takes so long
it should "sort in parallel" in {
import scala.concurrent.ExecutionContext.Implicits.global
val r = Random
val list = LazyList.from(1).take(10000).map(_ => r.nextInt())
val sorted = Sorted.create(list)
val xsf = sorted.parallel
whenReady(xsf, Timeout(Span(2, Seconds))) {
case xs if !Sorted.verify(xs) => fail("not sorted")
case _ =>
}
}

it should "merge sort large in parallel" in {
import scala.concurrent.ExecutionContext.Implicits.global
val r = Random
val list = LazyList.from(1).take(1000).map(_ => r.nextInt())
import Sorted._
val xsf = mergeSort(list)
whenReady(xsf) { xs => verify(xs) }
}

}

34 changes: 34 additions & 0 deletions src/it/scala/com/phasmidsoftware/sort/SortingFuncSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019. Phasmid Software. Comparer: functional comparison library.
*/

package com.phasmidsoftware.sort

import com.phasmidsoftware.util.RandomState
import org.scalatest.concurrent.{Futures, ScalaFutures}
import org.scalatest.{flatspec, matchers}


/**
* @author scalaprof
*/
class SortingFuncSpec extends flatspec.AnyFlatSpec with matchers.should.Matchers with Futures with ScalaFutures {

behavior of "Quick Sort"

it should "sort List[Long]" in {
val list = RandomState(0L).stream.take(1000).toList
val ordered = list.sorted
ordered.take(5) shouldBe Array(-9216660707259175019L, -9213036029692010464L, -9206926828484168278L, -9181452172432867415L, -9162073249424238457L)
ordered.reverse.take(5) shouldBe Array(9195196935716632702L, 9181861996276601465L, 9179403599629101794L, 9174553474901295787L, 9151076946114612916L)
}

behavior of "Merge Sort"

it should "sort List[Long]" in {
val array = RandomState(0L).stream.take(1000).toArray
Sorting.mergeSort(array)
Sorted.verify(array.toList) shouldBe true
array.reverse.take(5) shouldBe Array(9195196935716632702L, 9181861996276601465L, 9179403599629101794L, 9174553474901295787L, 9151076946114612916L)
}
}
49 changes: 33 additions & 16 deletions src/main/scala/com/phasmidsoftware/comparer/Comparers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.phasmidsoftware.generic.TupleWrangler._

import scala.util.Try


/**
* Definitions of the various comparers for different underlying types.
*
Expand All @@ -23,8 +22,7 @@ trait Comparers {
* @tparam T the underlying type of the inputs.
* @return a Comparer of Iterable[T] which can compare two instances of Iterable[T] and return a Comparison.
*/
implicit def comparerIterable[T: Comparer]: Comparer[Iterable[T]] = to2 => to1 =>
(to1 zip to2).foldLeft[Comparison](Same)((a, x) => a orElse Compare(x._1, x._2))
implicit def comparerIterable[T: Comparer]: Comparer[Iterable[T]] = ts2 => ts1 => comparePrefixes(ts1, ts2)

/**
* Method to return a Comparer[Iterable[T] where T is any type that has an implicit Comparer.
Expand All @@ -36,8 +34,8 @@ trait Comparers {
*/
implicit def comparerSeq[T: Comparer]: Comparer[Seq[T]] = {
// NOTE: this construction is necessary to avoid diverging implicit expansion compiler error: don't inline.
val comparer: Comparer[Iterable[T]] = comparerIterable
comparer.snap(identity)
val tsc: Comparer[Iterable[T]] = comparerIterable
tsc.snap(identity)
}

/**
Expand All @@ -50,8 +48,8 @@ trait Comparers {
*/
implicit def comparerList[T: Comparer]: Comparer[List[T]] = {
// NOTE: this construction is necessary to avoid diverging implicit expansion compiler error: don't inline.
val comparer: Comparer[Iterable[T]] = comparerIterable
comparer.snap(identity)
val tsc: Comparer[Iterable[T]] = comparerIterable
tsc.snap(identity)
}

/**
Expand All @@ -64,8 +62,8 @@ trait Comparers {
*/
implicit def comparerArray[T: Comparer]: Comparer[Array[T]] = {
// NOTE: this construction is necessary to avoid diverging implicit expansion compiler error: don't inline.
val comparer: Comparer[Iterable[T]] = comparerIterable
comparer.snap(x => x)
val tsc: Comparer[Iterable[T]] = comparerIterable
tsc.snap(x => x)
}

/**
Expand All @@ -77,13 +75,7 @@ trait Comparers {
* @return a Comparer of Option[T] which can compare two instances of Option[T] and return a Comparison.
*/
implicit def comparerOpt[T: Comparer]: Comparer[Option[T]] = to1 => to2 =>
to1 match {
case Some(t1) => to2 match {
case Some(t2) => implicitly[Comparer[T]].apply(t1)(t2)
case None => Same
}
case None => Same
}
(for (t1 <- to1; t2 <- to2) yield implicitly[Comparer[T]].apply(t1)(t2)).getOrElse(Same)

/**
* Method to return a Comparer[Try[T] where T is any type that has an implicit Comparer.
Expand Down Expand Up @@ -290,5 +282,30 @@ trait Comparers {
def comparer11[P0: Comparer, P1: Comparer, P2: Comparer, P3: Comparer, P4: Comparer, P5: Comparer, P6: Comparer, P7: Comparer, P8: Comparer, P9: Comparer, P10: Comparer, T <: Product](f: (P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) => T): Comparer[T] =
comparer10(strip(f)) orElse comparer[T, P10](10)

/**
* Method to compare two Iterables such that only the common prefixes are compared.
*
* @param ts1 an Iterable[T].
* @param ts2 an Iterable[T].
* @tparam T the underlying type.
* @return a Comparison.
*/
def comparePrefixes[T: Comparer](ts1: Iterable[T], ts2: Iterable[T]): Comparison =
(ts1 zip ts2).foldLeft[Comparison](Same) {
case (c, (t1, t2)) => c orElse Compare(t1, t2)
}

/**
* Method to compare two Iterables such that, if the common prefixes are the same,
* the "larger" Iterable is considered to be the longer one.
*
* @param ts1 an Iterable[T].
* @param ts2 an Iterable[T].
* @tparam T the underlying type.
* @return a Comparison.
*/
def compareAll[T: Comparer](ts1: Iterable[T], ts2: Iterable[T]): Comparison =
comparePrefixes(ts1, ts2) orElse implicitly[Comparer[Int]].compare((ts1.size, ts2.size))

private def comparer[T <: Product, P: Comparer](x: Int): Comparer[T] = Comparer.comparer[T, P](t => t.productElement(x).asInstanceOf[P])
}
31 changes: 7 additions & 24 deletions src/main/scala/com/phasmidsoftware/comparer/Comparison.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ sealed trait Comparison extends (() => Kleenean) {
*/
def apply(): Kleenean

/**
* Definition of a function which takes Unit and returns String.
*
* NOTE: I'm not entirely clear why this is even here.
*/
val f: () => String

/**
* Method to yield logical AND.
*
Expand Down Expand Up @@ -110,8 +103,6 @@ case class Different(less: Boolean) extends Comparison {
*/
def apply(): Kleenean = Truth(less)

val f: () => String = () => toString()

/**
* Short-circuited AND.
*
Expand Down Expand Up @@ -170,30 +161,25 @@ case object Same extends Comparison {
def apply(): Kleenean = Maybe

/**
* Not sure why we have this.
*/
val f: () => String = () => toString()

/**
* Short-circuited AND -- but in this case there is no short circuit.
* Short-circuited AND -- but in this case (Same) there is no short circuit.
*
* @param c the other Comparison (always evaluated).
* @return c & this.
*/
def &&(c: => Comparison): Comparison = c match {
case Same => Same
case Different(b) => if (b) c else Same
case Different(true) => c
case _ => Same
}

/**
* Short-circuited OR -- but in this case there is no short circuit.
* Short-circuited OR -- but in this case (Same) there is no short circuit.
*
* @param c the other Comparison (always evaluated).
* @return c | this.
*/
def ||(c: => Comparison): Comparison = c match {
case Same => Same
case Different(b) => if (b) Same else c
case Different(false) => c
case _ => Same
}

/**
Expand Down Expand Up @@ -264,10 +250,7 @@ object Comparison {
* @param bo an optional Boolean.
* @return the homologous Comparison for the input.
*/
def apply(bo: Option[Boolean]): Comparison = bo match {
case Some(b) => Comparison(b);
case _ => Same
}
def apply(bo: Option[Boolean]): Comparison = (bo map Comparison.apply).getOrElse(Same)

/**
* Method to construct a Comparison from a Java-style comparison result.
Expand Down
Loading