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

lucuma.core.instances.TreeMapInstances.scala Maven / Gradle / Ivy

There is a newer version: 0.108.0
Show newest version
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.core.instances

import cats.Always
import cats.Applicative
import cats.Eval
import cats.FlatMap
import cats.Foldable
import cats.Monoid
import cats.Show
import cats.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 {

  given [K: Hash: Order, V: Hash]: Hash[TreeMap[K, V]] =
    new TreeMapHash[K, V]

  given [K: Order, V: CommutativeSemigroup]
    : CommutativeMonoid[TreeMap[K, V]] =
    new TreeMapCommutativeMonoid[K, V]

  given [A, B](using
    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(", ", ", ")")
    }

  private type TreeMapInstancesType[K] = Traverse[TreeMap[K, *]] & FlatMap[TreeMap[K, *]]
  given [K: Order]: TreeMapInstancesType[K] =
    new Traverse[TreeMap[K, *]] with FlatMap[TreeMap[K, *]] {

      given Ordering[K] = Order[K].toOrdering

      def traverse[G[_], A, B](
        fa: TreeMap[K, A]
      )(f:  A => G[B])(using 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])(using 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 {
  given [K: Order, V: Eq]: Eq[TreeMap[K, V]] =
    new TreeMapEq[K, V]
}

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

class TreeMapHash[K, V](using V: Hash[V], O: Order[K], K: Hash[K])
    extends TreeMapEq[K, V]
    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](using 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](using V: CommutativeSemigroup[V], O: Order[K])
    extends TreeMapMonoid[K, V]
    with CommutativeMonoid[TreeMap[K, V]]

class TreeMapMonoid[K, V](using 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