All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gem.instances.TreeMapInstances.scala Maven / Gradle / Ivy

The newest version!
// Copyright (c) 2016-2020 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package gem.instances

import cats.{Always, Applicative, Eval, FlatMap, Foldable, Monoid, Show, Traverse}
import cats.kernel._
import cats.kernel.instances.StaticMethods
import scala.annotation.tailrec
import scala.collection.immutable.TreeMap

// This redefines all the cats SortedMap instances for TreeMap. It's a copy/paste from the cats
// source. We may want to go back and do everything in terms of SortedMap … not sure we need
// TreeMap specifically. TBD.
trait TreeMapInstances extends TreeMapInstances2 {

  implicit def catsStdHashForTreeMap[K: Hash: Order, V: Hash]: Hash[TreeMap[K, V]] =
    new TreeMapHash[K, V]

  implicit def catsStdCommutativeMonoidForTreeMap[K: Order, V: CommutativeSemigroup]: CommutativeMonoid[TreeMap[K, V]] =
    new TreeMapCommutativeMonoid[K, V]

  implicit def catsStdShowForTreeMap[A: Order, B](implicit showA: Show[A], showB: Show[B]): Show[TreeMap[A, B]] =
    new Show[TreeMap[A, B]] {
      def show(m: TreeMap[A, B]): String =
        m.iterator
          .map { case (a, b) => showA.show(a) + " -> " + showB.show(b) }
          .mkString("TreeMap(", ", ", ")")
    }

  implicit def catsStdInstancesForTreeMap[K: Order]: Traverse[TreeMap[K, ?]] with FlatMap[TreeMap[K, ?]] =
    new Traverse[TreeMap[K, ?]] with FlatMap[TreeMap[K, ?]] {

      implicit val orderingK: Ordering[K] = Order[K].toOrdering

      def traverse[G[_], A, B](fa: TreeMap[K, A])(f: A => G[B])(implicit G: Applicative[G]): G[TreeMap[K, B]] = {
        val gba: Eval[G[TreeMap[K, B]]] = Always(G.pure(TreeMap.empty(Order[K].toOrdering)))
        Foldable.iterateRight(fa, gba){ (kv, lbuf) =>
          G.map2Eval(f(kv._2), lbuf)({ (b, buf) => buf + (kv._1 -> b)})
        }.value
      }

      def flatMap[A, B](fa: TreeMap[K, A])(f: A => TreeMap[K, B]): TreeMap[K, B] =
        fa.flatMap { case (k, a) => f(a).get(k).map((k, _)) }

      override def map[A, B](fa: TreeMap[K, A])(f: A => B): TreeMap[K, B] =
        fa.map { case (k, a) => (k, f(a)) }


      override def map2Eval[A, B, Z](fa: TreeMap[K, A], fb: Eval[TreeMap[K, B]])(f: (A, B) => Z): Eval[TreeMap[K, Z]] =
        if (fa.isEmpty) Eval.now(TreeMap.empty(Order[K].toOrdering)) // no need to evaluate fb
        else fb.map(fb => map2(fa, fb)(f))

      override def ap2[A, B, Z](f: TreeMap[K, (A, B) => Z])(fa: TreeMap[K, A], fb: TreeMap[K, B]): TreeMap[K, Z] =
        f.flatMap { case (k, f) =>
          for { a <- fa.get(k); b <- fb.get(k) } yield (k, f(a, b))
        }

      def foldLeft[A, B](fa: TreeMap[K, A], b: B)(f: (B, A) => B): B =
        fa.foldLeft(b) { case (x, (_, a)) => f(x, a)}

      def foldRight[A, B](fa: TreeMap[K, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
        Foldable.iterateRight(fa.values, lb)(f)

      def tailRecM[A, B](a: A)(f: A => TreeMap[K, Either[A, B]]): TreeMap[K, B] = {
        val bldr = TreeMap.newBuilder[K, B](Order[K].toOrdering)

        @tailrec def descend(k: K, either: Either[A, B]): Unit =
          either match {
            case Left(a) =>
              f(a).get(k) match {
                case Some(x) => descend(k, x)
                case None => ()
              }
            case Right(b) =>
              bldr += ((k, b))
              ()
          }

        f(a).foreach { case (k, a) => descend(k, a) }
        bldr.result
      }

      override def size[A](fa: TreeMap[K, A]): Long = fa.size.toLong

      override def get[A](fa: TreeMap[K, A])(idx: Long): Option[A] =
        if (idx < 0L || Int.MaxValue < idx) None
        else {
          val n = idx.toInt
          if (n >= fa.size) None
          else Some(fa.valuesIterator.drop(n).next)
        }

      override def isEmpty[A](fa: TreeMap[K, A]): Boolean = fa.isEmpty

      override def fold[A](fa: TreeMap[K, A])(implicit A: Monoid[A]): A =
        A.combineAll(fa.values)

      override def toList[A](fa: TreeMap[K, A]): List[A] = fa.values.toList

      override def collectFirst[A, B](fa: TreeMap[K, A])(pf: PartialFunction[A, B]): Option[B] = fa.collectFirst(new PartialFunction[(K, A), B] {
        override def isDefinedAt(x: (K, A)) = pf.isDefinedAt(x._2)
        override def apply(v1: (K, A)) = pf(v1._2)
      })

      override def collectFirstSome[A, B](fa: TreeMap[K, A])(f: A => Option[B]): Option[B] = collectFirst(fa)(Function.unlift(f))
    }

}

trait TreeMapInstances1 {
  implicit def catsStdEqForTreeMap[K: Order, V: Eq]: Eq[TreeMap[K, V]] =
    new TreeMapEq[K, V]
}

trait TreeMapInstances2 extends TreeMapInstances1 {
  implicit def catsStdMonoidForTreeMap[K: Order, V: Semigroup]: Monoid[TreeMap[K, V]] =
    new TreeMapMonoid[K, V]
}

class TreeMapHash[K, V](implicit V: Hash[V], O: Order[K], K: Hash[K]) extends TreeMapEq[K, V]()(V, O) with Hash[TreeMap[K, V]] {
  // adapted from [[scala.util.hashing.MurmurHash3]],
  // but modified standard `Any#hashCode` to `ev.hash`.
  import scala.util.hashing.MurmurHash3._
  def hash(x: TreeMap[K, V]): Int = {
    var a, b, n = 0
    var c = 1;
    x foreach { case (k, v) =>
      val h = StaticMethods.product2Hash(K.hash(k), V.hash(v))
      a += h
      b ^= h
      if (h != 0) c *= h
      n += 1
    }
    var h = mapSeed
    h = mix(h, a)
    h = mix(h, b)
    h = mixLast(h, c)
    finalizeHash(h, n)
  }
}

class TreeMapEq[K, V](implicit V: Eq[V], O: Order[K]) extends Eq[TreeMap[K, V]] {
  def eqv(x: TreeMap[K, V], y: TreeMap[K, V]): Boolean =
    if (x eq y) (O, true)._2
    else x.size == y.size && x.forall { case (k, v1) =>
      y.get(k) match {
        case Some(v2) => V.eqv(v1, v2)
        case None => false
      }
    }
}

class TreeMapCommutativeMonoid[K, V](implicit V: CommutativeSemigroup[V], O: Order[K])
  extends TreeMapMonoid[K, V] with CommutativeMonoid[TreeMap[K, V]]

class TreeMapMonoid[K, V](implicit V: Semigroup[V], O: Order[K]) extends Monoid[TreeMap[K, V]]  {

  def empty: TreeMap[K, V] = TreeMap.empty(O.toOrdering)

  def combine(xs: TreeMap[K, V], ys: TreeMap[K, V]): TreeMap[K, V] =
    if (xs.size <= ys.size) {
      xs.foldLeft(ys) { case (my, (k, x)) =>
        my.updated(k, Semigroup.maybeCombine(x, my.get(k)))
      }
    } else {
      ys.foldLeft(xs) { case (mx, (k, y)) =>
        mx.updated(k, Semigroup.maybeCombine(mx.get(k), y))
      }
    }

}

object treemap extends TreeMapInstances




© 2015 - 2024 Weber Informatics LLC | Privacy Policy