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

scalaz.Map.scala Maven / Gradle / Ivy

The newest version!
package scalaz

/**
 * @see [[https://hackage.haskell.org/package/containers-0.5.7.1/docs/mini_Data-Map-Strict.html]]
 * @see [[https://github.com/haskell/containers/blob/v0.5.7.1/Data/Map/Base.hs]]
 */
import Ordering.{ EQ, LT, GT }

import std.anyVal._
import Maybe.{ Empty, Just, just }

import annotation.tailrec

/** An immutable map of key/value pairs implemented as a balanced binary tree
 *
 * Based on Haskell's Data.Map
 *
 * @since 7.0.3 */
sealed abstract class ==>>[A, B] {
  import ==>>._

  /** number of key/value pairs - O(1) */
  val size: Int

  /** returns `true` if this map contains no key/value pairs - O(1) */
  def isEmpty: Boolean = this == empty

  /** tupled form of [[insert]] */
  def + (a: (A, B))(implicit o: Order[A]): A ==>> B =
    insert(a._1, a._2)

  /** inserts a new key/value - O(log n).
   *
   * If the key is already present, its value is replaced by the provided value.  */
  def insert(kx: A, x: B)(implicit n: Order[A]): A ==>> B =
    this match {
      case Tip() =>
        singleton(kx, x)
      case Bin(ky, y, l, r) =>
        n.order(kx, ky) match {
          case LT =>
            balanceL(ky, y, l.insert(kx, x), r)
          case GT =>
            balanceR(ky, y, l, r.insert(kx, x))
          case EQ =>
            Bin(kx, x, l, r)
        }
    }

  /** inserts a new key/value pair, resolving the conflict if the key already exists - O(log n)
   *
   * @param f function to resolve conflict with existing key:
   *   (insertedValue, existingValue) => resolvedValue
   * @param kx key
   * @param x value to insert if the key is not already present */
  def insertWith(f: (B, B) => B, kx: A, x: B)(implicit o: Order[A]): A ==>> B =
    insertWithKey((_, a, b) => f(a,b), kx, x)

  /** inserts a new key/value pair, resolving the conflict if the key already exists - O(log n)
   *
   * @param f function to resolve conflict with existing key:
   *   (key, insertedValue, existingValue) => resolvedValue
   * @param kx key
   * @param x value to insert if the key is not already present */
  def insertWithKey(f: (A, B, B) => B, kx: A, x: B)(implicit o: Order[A]): A ==>> B =
    this match {
      case Tip() =>
        singleton(kx, x)
      case Bin(ky, y, l, r) =>
        o.order(kx, ky) match {
          case LT =>
            balanceL(ky, y, l.insertWithKey(f, kx, x), r)
          case GT =>
            balanceR(ky, y, l, r.insertWithKey(f, kx, x))
          case EQ =>
            Bin(kx, f(kx, x, y), l, r)
        }
    }

  def filterM[F[_]](f: B => F[Boolean])(implicit F: Applicative[F], O:Order[A]):F[A ==>> B] = this match {
    case Tip() => F.pure(empty)
    case Bin(kx, x, l ,r) =>
      F.ap(F.tuple2(l.filterM(f),r.filterM(f)))(
        F.map(f(x))(b => (tpl:(A ==>>B, A ==>> B)) => if (b) Bin(kx, x, tpl._1, tpl._2) else tpl._1.union(tpl._2))
      )
  }

  /** alias for [[delete]] */
  def -(k: A)(implicit o: Order[A]): A ==>> B =
    delete(k)

  /** removes a key/value pair - O(log n) */
  def delete(k: A)(implicit n: Order[A]): A ==>> B =
    this match {
      case Tip() =>
        empty
      case Bin(kx, x, l, r) =>
        n.order(k, kx) match {
          case LT =>
            balanceR(kx, x, l.delete(k), r)
          case EQ =>
            glue(l, r)
          case GT =>
            balanceL(kx, x, l, r.delete(k))
        }
    }

  /** if the key exists, transforms its value - O(log n) */
  def adjust(k: A, f: B => B)(implicit o: Order[A]): A ==>> B =
    adjustWithKey(k, (_, x) => f(x))

  /** like [[adjust]] but with the key available in the transformation - O(log n) */
  def adjustWithKey(k: A, f: (A, B) => B)(implicit o: Order[A]): A ==>> B =
    updateWithKey(k, (a, b) => just(f(a, b)))

  /** updates or removes a value - O(log n)
   *
   * if `f` returns `None`, then the key is removed from the map */
  def update(k: A, f: B => Maybe[B])(implicit o: Order[A]): A ==>> B =
    updateWithKey(k, (_, x) => f(x))

  /** like [[update]] but with the key available in the update function - O(log n) */
  def updateWithKey(k: A, f: (A, B) => Maybe[B])(implicit o: Order[A]): A ==>> B =
    this match {
      case Tip() =>
        empty
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            balanceR(kx, x, l.updateWithKey(k, f), r)
          case GT =>
            balanceL(kx, x, l, r.updateWithKey(k, f))
          case EQ =>
            f(kx, x) match {
              case Just(v) =>
                Bin(kx, v, l, r)
              case Empty() =>
                glue(l, r)
            }
        }
    }

  /** looks up a key and updates its value - O(log n)
   *
   * Similar to [[updateWithKey]] but also returns the value. If the value was updated, returns the
   * new value. If the value was deleted, returns the old value. */
  def updateLookupWithKey(k: A, f: (A, B) => Maybe[B])(implicit o: Order[A]): (Maybe[B], A ==>> B) =
    this match {
      case Tip() =>
        (Maybe.empty, empty)
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            val (found, ll) = l.updateLookupWithKey(k, f)
            (found, balanceR(kx, x, ll, r))
          case GT =>
            val (found, rr) = r.updateLookupWithKey(k, f)
            (found, balanceL(kx, x, l, rr))
          case EQ =>
            f(kx, x) match {
              case Just(xx) =>
                (just(xx), Bin(kx, xx, l, r))
              case Empty() =>
                (just(x), glue(l, r))
            }
        }
    }

  def alter(k: A, f: Maybe[B] => Maybe[B])(implicit o: Order[A]): A ==>> B =
    this match {
      case Tip() =>
        f(Maybe.empty) match {
          case Empty() =>
            empty
          case Just(x) =>
            singleton(k, x)
        }

      case Bin(kx, x, l,r) =>
        o.order(k, kx) match {
          case LT =>
            balance(kx, x, l.alter(k, f), r)
          case GT =>
            balance(kx, x, l, r.alter(k, f))
          case EQ =>
            f(just(x)) match {
              case Empty() =>
                glue(l, r)
              case Just(xx) =>
                Bin(kx, xx, l, r)
            }
        }
    }

  @tailrec
  final def lookup(k: A)(implicit n: Order[A]): Maybe[B] =
    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l, r) =>
        n.order(k, kx) match {
          case LT =>
            l.lookup(k)
          case GT =>
            r.lookup(k)
          case EQ =>
            just(x)
        }
    }

  @tailrec
  final def lookupAssoc(k: A)(implicit n: Order[A]): Maybe[(A, B)] =
    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l, r) =>
        n.order(k, kx) match {
          case LT =>
            l.lookupAssoc(k)
          case GT =>
            r.lookupAssoc(k)
          case EQ =>
            just((kx, x))
        }
    }

  @tailrec
  final def lookupLT(k: A)(implicit o: Order[A]): Maybe[(A, B)] = {
    @tailrec
    def goSome(kx: A, x: B, t: A ==>> B): Maybe[(A, B)] =
      t match {
        case Tip() =>
          just((kx, x))
        case Bin(ky, y, l, r) =>
          if (o.lessThanOrEqual(k, ky)) goSome(kx, x, l) else goSome(ky, y, r)
      }

    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l, r) =>
        if (o.lessThanOrEqual(k, kx)) l.lookupLT(k) else goSome(kx, x, r)
    }
  }

  @tailrec
  final def lookupGT(k: A)(implicit o: Order[A]): Maybe[(A, B)] = {
    @tailrec
    def goSome(kx: A, x: B, t: A ==>> B): Maybe[(A, B)] =
      t match {
        case Tip() =>
          just((kx, x))
        case Bin(ky, y, l, r) =>
          if (o.greaterThanOrEqual(k, ky)) goSome(kx, x, r) else goSome(ky, y, l)
      }

    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l, r) =>
        if (o.greaterThanOrEqual(k, kx)) r.lookupGT(k) else goSome(kx, x, l)
    }
  }

  @tailrec
  final def lookupLE(k: A)(implicit o: Order[A]): Maybe[(A, B)] = {
    @tailrec
    def goSome(kx: A, x: B, t: A ==>> B): Maybe[(A, B)] =
      t match {
        case Tip() =>
          just((kx, x))
        case Bin(ky, y, l, r) =>
          o.order(k, ky) match {
            case LT =>
              goSome(kx, x, l)
            case EQ =>
              just((ky, y))
            case GT =>
              goSome(ky, y, r)
          }
      }

    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            l.lookupLE(k)
          case EQ =>
            just((kx, x))
          case GT =>
            goSome(kx, x, r)
        }
    }
  }

  @tailrec
  final def lookupGE(k: A)(implicit o: Order[A]): Maybe[(A, B)] = {
    @tailrec
    def goSome(kx: A, x: B, t: A ==>> B): Maybe[(A, B)] =
      t match {
        case Tip() =>
          just((kx, x))
        case Bin(ky, y, l, r) =>
          o.order(k, ky) match {
            case LT =>
              goSome(ky, y, l)
            case EQ =>
              just((ky, y))
            case GT =>
              goSome(kx, x, r)
          }
      }

    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            goSome(kx, x, l)
          case EQ =>
            just((kx, x))
          case GT =>
            r.lookupGE(k)
        }
    }
  }

  def values: IList[B] =
    foldrWithKey(IList.empty[B])((_, x, xs) => x :: xs)

  def keys: IList[A] =
    foldrWithKey(IList.empty[A])((x, _, xs) => x :: xs)

  def keySet: ISet[A] = this match {
    case Tip()        => ISet.Tip[A]()
    case Bin(k,v,l,r) => ISet.Bin(k,l.keySet,r.keySet)
  }

  def toList: List[(A, B)] =
    toAscList

  def toIList: IList[(A, B)] =
    toAscIList

  def toAscList: List[(A, B)] =
    foldrWithKey(List.empty[(A, B)])((k, x, xs) => (k, x) :: xs)

  def toAscIList: IList[(A, B)] =
    foldrWithKey(IList.empty[(A, B)])((k, x, xs) => (k, x) :: xs)

  def toDescList: List[(A, B)] =
    foldlWithKey(List.empty[(A, B)])((xs, k, x) => (k, x) :: xs)

  def toDescIList: IList[(A, B)] =
    foldlWithKey(IList.empty[(A, B)])((xs, k, x) => (k, x) :: xs)

  def member(k: A)(implicit n: Order[A]): Boolean =
    lookup(k)(n).isJust

  def notMember(k: A)(implicit n: Order[A]): Boolean =
    !member(k)

  def lookupIndex(k: A)(implicit o: Order[A]): Maybe[Int] = {
    @tailrec
    def go(n: Int, m: A ==>> B): Maybe[Int] =
      m match {
        case Tip() =>
          Maybe.empty
        case Bin(kx, x, l, r) =>
          o.order(k, kx) match {
            case LT =>
              go(n, l)
            case GT =>
              go(n + l.size + 1, r)
            case EQ =>
              just((n + l.size))
          }
      }

    go(0, this)
  }

  @tailrec
  final def elemAt(i: Int): Maybe[(A, B)] =
    this match {
      case Tip() =>
        Maybe.empty
      case Bin(kx, x, l ,r) =>
        implicitly[Order[Int]].order(i, l.size) match {
          case LT =>
            l.elemAt(i)
          case GT =>
            r.elemAt(i - l.size - 1)
          case EQ =>
            just((kx, x))
        }
    }

  // TODO: This should be a total function
  def updateAt(i: Int, f: (A, B) => Maybe[B]): A ==>> B =
    this match {
      case Tip() =>
        sys.error("updateAt")
      case Bin(kx, x, l, r) =>
        implicitly[Order[Int]].order(i, l.size) match {
          case LT =>
            balanceR(kx, x, l.updateAt(i, f), r)
          case GT =>
            balanceL(kx, x, l, r.updateAt(i - l.size - 1, f))
          case EQ =>
            f(kx, x) match {
              case Just(y) => Bin(kx, y, l, r)
              case Empty() => glue(l, r)
            }
        }
    }

  def deleteAt(i: Int): A ==>> B =
    updateAt(i, (A, B) => Maybe.empty)

  @tailrec
  final def findMin: Maybe[(A, B)] =
    this match {
      case Bin(kx, x, Tip(), _) =>
        just((kx, x))
      case Bin(_, _, l, _) =>
        l.findMin
      case Tip() =>
        Maybe.empty
    }

  @tailrec
  final def findMax: Maybe[(A, B)] =
    this match {
      case Bin(kx, x, _, Tip()) =>
        just((kx, x))
      case Bin(_, _, _, r) =>
        r.findMax
      case Tip() =>
        Maybe.empty
    }

  def deleteMin: A ==>> B =
    this match {
      case Bin(_, _, Tip(), r) =>
        r
      case Bin(kx, x, l, r) =>
        balanceR(kx, x, l.deleteMin, r)
      case Tip() =>
        empty
    }

  def deleteMax: A ==>> B =
    this match {
      case Bin(_, _, l, Tip()) =>
        l
      case Bin(kx, x, l, r) =>
        balanceL(kx, x, l, r.deleteMax)
      case Tip() =>
        empty
    }

  def updateMin(f: B => Maybe[B]): A ==>> B =
    updateMinWithKey((_: A, b) => f(b))

  def updateMinWithKey(f: (A, B) => Maybe[B]): A ==>> B =
    this match {
      case Bin(kx, x, Tip(), r) =>
        f(kx, x) match {
          case Empty() =>
            r
          case Just(s) =>
            Bin(kx, s, Tip(), r)
        }
      case Bin(kx, x, l, r) =>
        balanceR(kx, x, l.updateMinWithKey(f), r)
      case Tip() =>
        empty
    }

  def updateMax(f: B => Maybe[B]): A ==>> B =
    updateMaxWithKey((_: A, b) => f(b))

  def updateMaxWithKey(f: (A, B) => Maybe[B]): A ==>> B =
    this match {
      case Bin(kx, x, l, Tip()) =>
        f(kx, x) match {
          case Empty() =>
            l
          case Just(s) =>
            Bin(kx, s, l, Tip())
        }
      case Bin(kx, x, l, r) =>
        balanceL(kx, x, l, r.updateMaxWithKey(f))
      case Tip() =>
        empty
    }

  /**
    * insert v into the map at k. If there is already a value for k,
    * append to the existing value using the Semigroup
    */
  def updateAppend(k: A, v: B)(implicit o: Order[A], bsg: Semigroup[B]): A ==>> B =
    alter(k, old => just(old.map(bsg.append(_, v)).getOrElse(v)))

  def minViewWithKey: Maybe[((A, B), A ==>> B)] =
    this match {
      case Tip() =>
        Maybe.empty
      case x @ Bin(_, _, _, _) =>
        just(deleteFindMin(x))
    }

  def maxViewWithKey: Maybe[((A, B), A ==>> B)] =
    this match {
      case Tip() =>
        Maybe.empty
      case x @ Bin(_, _, _, _) =>
        just(deleteFindMax(x))
    }

  def minView: Maybe[(B, A ==>> B)] =
    this match {
      case Tip() =>
        Maybe.empty
      case x @ Bin(_, _, _, _) =>
        val r = deleteFindMin(x)
        just((r._1._2, r._2))
    }

  def maxView: Maybe[(B, A ==>> B)] =
    this match {
      case Tip() =>
        Maybe.empty
      case x @ Bin(_, _, _, _) =>
        val r = deleteFindMax(x)
        just((r._1._2, r._2))
    }

  private def deleteFindMax(t: Bin[A, B]): ((A, B), A ==>> B) =
    t match {
      case Bin(k, x, l, Tip()) =>
        ((k,x), l)
      case Bin(k, x, l, r @ Bin(_, _, _, _)) =>
        val (km, r2) = deleteFindMax(r)
        (km, balanceL(k, x, l, r2))
    }

  private def deleteFindMin(t: Bin[A, B]): ((A, B), A ==>> B) =
    t match {
      case Bin(k, x, Tip(), r) =>
        ((k, x), r)
      case Bin(k, x, l @ Bin(_, _, _, _), r) =>
        val (km, l2) = deleteFindMin(l)
        (km, balanceR(k, x, l2, r))
    }

  /* Mappings */
  def map[C](f: B => C): A ==>> C =
    mapWithKey((_, x: B) => f(x))

  def mapWithKey[C](f: (A, B) => C): A ==>> C =
    this match {
      case Tip() =>
        empty
      case Bin(kx, x, l, r) =>
        Bin(kx, f(kx, x), l.mapWithKey(f), r.mapWithKey(f))
    }

  def traverseWithKey[F[_], C](f: (A, B) => F[C])(implicit G: Applicative[F]): F[A ==>> C] =
    this match {
      case Tip() =>
        G.point(Tip())
      case Bin(kx, x, Tip(), Tip()) =>
        G.apply(f(kx, x)) { x2 =>
          Bin(kx, x2, Tip(), Tip())
        }
      case Bin(kx, x, l, r) =>
        G.apply3(l.traverseWithKey(f), f(kx, x), r.traverseWithKey(f)) {
          (l2, x2, r2) => Bin(kx, x2, l2, r2)
        }
    }

  def mapAccum[C](z: C)(f: (C, B) => (C, B)): (C, A ==>> B) =
    mapAccumWithKey(z)((a2, _, x2) => f(a2, x2))

  def mapAccumWithKey[C](z: C)(f: (C, A, B) => (C, B)): (C, A ==>> B) =
    mapAccumL(z)(f)

  def mapAccumL[C](a: C)(f: (C, A, B) => (C, B)): (C, A ==>> B) =
    this match {
      case Tip() =>
        (a, empty)
      case Bin(kx, x, l, r) =>
        val (a1, l2) = l.mapAccumL(a)(f)
        val (a2, x2) = f(a1, kx, x)
        val (a3, r2) = r.mapAccumL(a2)(f)
        (a3, Bin(kx, x2, l2, r2))
    }

  //def mapAccumRWithKey

  def mapKeys[C](f: A => C)(implicit o: Order[C]): C ==>> B =
    foldlWithKey(empty[C, B])((xs, k, x) => xs.insert(f(k), x))

  def mapKeysWith[C](f: A => C, f2: (B, B) => B)(implicit o: Order[C]): C ==>> B =
    fromListWith[C, B](toList.map(x => (f(x._1), x._2)))(f2)

  /* Folds */
  def fold[C](z: C)(f: (A, B, C) => C): C =
    foldrWithKey(z)(f)

  def foldlWithKey[C](z: C)(f:  (C, A, B) => C): C =
    this match {
      case Tip() =>
        z
      case Bin(kx, x, l, r) =>
        r.foldlWithKey(f(l.foldlWithKey(z)(f), kx, x))(f)
    }

  def foldrWithKey[C](z: C)(f: (A, B, C) => C): C =
    this match {
      case Tip() =>
        z
      case Bin(kx, x, l, r) =>
        l.foldrWithKey(f(kx, x, r.foldrWithKey(z)(f)))(f)
    }

  def foldMapWithKey[C](f: (A, B) => C)(implicit F: Monoid[C]): C =
    this match {
      case Tip() =>
        F.zero
      case Bin(k, x, Tip(), Tip()) =>
        f(k, x)
      case Bin(k, x, l, r) =>
        F.append(l.foldMapWithKey(f), F.append(f(k, x), r.foldMapWithKey(f)))
    }

  /* Unions */
  def union(other: A ==>> B)(implicit k: Order[A]): A ==>> B = {
    def hedgeUnion(blo: Maybe[A], bhi: Maybe[A], m1: A ==>> B, m2: A ==>> B): A ==>> B =
      (m1, m2) match {
        case (t1, Tip()) =>
          t1
        case (Tip(), Bin(kx, x, l, r)) =>
          link(kx, x, l filterGt blo, r filterLt bhi)
        case (t1, Bin(kx, x, Tip(), Tip())) =>
          insertR(kx, x, t1)
        case (Bin(kx, x, l, r), t2) =>
          val bmi = just(kx)
          val nm1 = hedgeUnion(blo, bmi, l, ==>>.trim(blo, bmi, t2))
          val nm2 = hedgeUnion(bmi, bhi, r, ==>>.trim(bmi, bhi, t2))
          link(kx, x, nm1, nm2)
      }

    def insertR(kx: A, x: B, t: A ==>> B)(implicit o: Order[A]): A ==>> B = {
      def go(kx: A, x: B, m: A ==>> B): A ==>> B =
        m match {
          case Tip() => singleton(kx, x)
          case Bin(ky, y, l, r) =>
            o.order(kx, ky) match {
              case LT => balanceL(ky, y, go(kx, x, l), r)
              case GT => balanceR(ky, y, l, go(kx, x, r))
              case EQ => m
            }
        }
      go(kx, x, t)
    }

    (this, other) match {
      case (Tip(), t2) => t2
      case (t1, Tip()) => t1
      case (t1, t2)    => hedgeUnion(Maybe.empty, Maybe.empty, t1, t2)
    }
  }

  def unionWith(other: A ==>> B)(f: (B, B) => B)(implicit o: Order[A]): A ==>> B =
    unionWithKey(other)((_, b, c) => f(b, c))

  def unionWithKey(other: A ==>> B)(f: (A, B, B) => B)(implicit o: Order[A]): A ==>> B =
    mergeWithKey(this, other)((a, b, c) => just(f(a, b, c)))(x => x, x => x)

  // Difference functions
  def \\[C](other: A ==>> C)(implicit o: Order[A]): A ==>> B =
    difference(other)

  def difference[C](other: A ==>> C)(implicit o: Order[A]): A ==>> B = {
    def hedgeDiff(blo: Maybe[A], bhi: Maybe[A], a: A ==>> B, b: A ==>> C): A ==>> B =
      (a, b) match {
        case (Tip(), _) =>
          empty
        case (Bin(kx, x, l, r), Tip()) =>
          link(kx, x, l filterGt blo, r filterLt bhi)
        case (t, Bin(kx, _, l, r)) =>
          val bmi = just(kx)
          val aa = hedgeDiff(blo, bmi, ==>>.trim(blo, bmi, t), l)
          val bb = hedgeDiff(bmi, bhi, ==>>.trim(bmi, bhi, t), r)
          aa merge bb
      }

    (this, other) match {
      case (Tip(), _) =>
        empty
      case (t1, Tip()) =>
        t1
      case (t1, t2) =>
        hedgeDiff(Maybe.empty, Maybe.empty, t1, t2)
    }
  }

  def differenceWith[C](other: A ==>> C)(f: (B, C) => Maybe[B])(implicit o: Order[A]): A ==>> B =
    differenceWithKey(other)((_, b, c) => f(b, c))

  def differenceWithKey[C](other: A ==>> C)(f: (A, B, C) => Maybe[B])(implicit o: Order[A]): A ==>> B =
    mergeWithKey(this, other)(f)(x => x, _ => empty)

  // Intersections
  def intersection[C](other: A ==>> C)(implicit o: Order[A]): A ==>> B = {
    def hedgeInt(blo: Maybe[A], bhi: Maybe[A], a: A ==>> B, b: A ==>> C): A ==>> B =
      (a, b) match {
        case (_, Tip()) =>
          empty
        case (Tip(), _) =>
          empty
        case (Bin(kx, x, l, r), t2) =>
          val bmi = just(kx)
          val l2 = hedgeInt(blo, bmi, l, ==>>.trim(blo, bmi, t2))
          val r2 = hedgeInt(bmi, bhi, r, ==>>.trim(bmi, bhi, t2))

          if (t2 member kx) link(kx, x, l2, r2)
          else l2 merge r2
      }

    (this, other) match {
      case (Tip(), _) => empty
      case (_, Tip()) => empty
      case (t1, t2)   => hedgeInt(Maybe.empty, Maybe.empty, t1, t2)
    }
  }

  def intersectionWith[C, D](other: A ==>> C)(f: (B, C) => D)(implicit o: Order[A]): A ==>> D =
    intersectionWithKey(other)((_, x, y) => f(x, y))

  def intersectionWithKey[C, D](other: A ==>> C)(f: (A, B, C) => D)(implicit o: Order[A]): A ==>> D =
    mergeWithKey(this, other)((a, b, c) => just(f(a, b, c)))(_ => empty, _ => empty)

  // Submap
  def isSubmapOf(a: A ==>> B)(implicit o: Order[A], e: Equal[B]): Boolean =
    isSubmapOfBy(a, e.equal)

  def isSubmapOfBy(a: A ==>> B, f: (B, B) => Boolean)(implicit o: Order[A]): Boolean =
    size <= a.size && submap(a, f)

  private[scalaz] def submap(a: A ==>> B, f: (B, B) => Boolean)(implicit o: Order[A]): Boolean =
    (this, a) match {
      case (Tip(), _) =>
        true
      case (_, Tip()) =>
        false
      case (Bin(kx, x, l, r), t) =>
        val (lt, found, gt) = t splitLookup kx
        found match {
          case Empty() =>
            false
          case Just(y) =>
            f(x, y) && l.submap(lt, f) && r.submap(gt, f)
        }
    }

  // Filter
  def filter(p: B => Boolean)(implicit o: Order[A]): A ==>> B =
    filterWithKey((_, x) => p(x))

  def filterWithKey(p: (A, B) => Boolean)(implicit o: Order[A]): A ==>> B =
    this match {
      case Tip() =>
        empty
      case Bin(kx, x, l, r) =>
        if (p(kx, x))
          link(kx, x, l.filterWithKey(p), r.filterWithKey(p))
        else
          l.filterWithKey(p) merge r.filterWithKey(p)
    }

  // Partition
  def partition(p: B => Boolean)(implicit o: Order[A]): (A ==>> B, A ==>> B) =
    partitionWithKey((_, x) => p(x))

  def partitionWithKey(p: (A, B) => Boolean)(implicit o: Order[A]): (A ==>> B, A ==>> B) =
    this match {
      case Tip() =>
        (empty, empty)
      case Bin(kx, x, l, r) =>
        val (l1, l2) = l partitionWithKey p
        val (r1, r2) = r partitionWithKey p

        if (p(kx, x))
          (link(kx, x, l1, r1), l2 merge r2)
        else
          (l1 merge r1, link(kx, x, l2, r2))
    }

  def mapMaybe[C](f: B => Maybe[C])(implicit o: Order[A]): A ==>> C =
    mapMaybeWithKey((_, x) => f(x))

  def mapMaybeWithKey[C](f: (A, B) => Maybe[C])(implicit o: Order[A]): A ==>> C =
    this match {
      case Tip() =>
        empty
      case Bin(kx, x, l, r) =>
        f(kx, x).cata(y =>
          link(kx, y, l.mapMaybeWithKey(f), r.mapMaybeWithKey(f)),
          l.mapMaybeWithKey(f).merge(r.mapMaybeWithKey(f))
        )
    }

  def mapEither[C, D](f: B => C \/ D)(implicit o: Order[A]): (A ==>> C, A ==>> D) =
    mapEitherWithKey((_, x) => f(x))

  def mapEitherWithKey[C, D](f: (A, B) => C \/ D)(implicit o: Order[A]): (A ==>> C, A ==>> D) =
    this match {
      case Tip() =>
        (empty, empty)
      case Bin(kx, x, l, r) =>
        val (l1, l2) = l.mapEitherWithKey(f)
        val (r1, r2) = r.mapEitherWithKey(f)

        f(kx, x) match {
          case -\/(y) =>
            (link(kx, y, l1, r1), l2 merge r2)
          case \/-(z) =>
            (l1 merge r1, link(kx, z, l2, r2))
        }
    }

  // Split
  def split(k: A)(implicit o: Order[A]): (A ==>> B, A ==>> B) =
    this match {
      case Tip() =>
        (empty, empty)
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            val (lt, gt) = l.split(k)
            (lt, link(kx, x, gt, r))
          case GT =>
            val (lt, gt) = r.split(k)
            (link(kx, x, l, lt), gt)
          case EQ =>
            (l, r)
        }
    }

  def splitLookup(k: A)(implicit o: Order[A]): (A ==>> B, Maybe[B], A ==>> B) =
    this match {
      case Tip() =>
        (empty, Maybe.empty, empty)
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            val (lt, z, gt) = l splitLookup k
            (lt, z, link(kx, x, gt, r))
          case GT =>
            val (lt, z, gt) = r splitLookup k
            (link(kx, x, l, lt), z, gt)
          case EQ =>
            (l, just(x), r)
        }
    }

  def splitLookupWithKey(k: A)(implicit o: Order[A]): (A ==>> B, Maybe[(A, B)], A ==>> B) =
    this match {
      case Tip() =>
        (empty, Maybe.empty, empty)
      case Bin(kx, x, l, r) =>
        o.order(k, kx) match {
          case LT =>
            val (lt, z, gt) = l splitLookupWithKey k
            (lt, z, link(kx, x, gt, r))
          case GT =>
            val (lt, z, gt) = r splitLookupWithKey k
            (link(kx, x, l, lt), z, gt)
          case EQ =>
            (l, just((kx, x)), r)
        }
    }

  def splitRoot: IList[A ==>> B] =
    this match {
      case Tip()           => IList.empty[A ==>> B]
      case Bin(k, x, l, r) => IList(l, singleton(k, x), r)
    }

  // Utility functions
  // Because the following functions depend on some public interface, such as deleteFindMax,
  // they can't be moved to object ==>> unlike other utility functions, balance(...) for example.

  // TODO: merge should be private as it's a utility function but not a public interface.
  protected def merge(other: A ==>> B): A ==>> B =
    (this, other) match {
      case (Tip(), r) =>
        r
      case (l, Tip()) =>
        l
      case (l @ Bin(kx, x, lx, rx), r @ Bin(ky, y, ly, ry)) =>
        if (delta * l.size < r.size) balanceL(ky, y, l.merge(ly), ry)
        else if (delta * r.size < l.size) balanceR(kx, x, lx, rx.merge(r))
        else glue(l, r)
    }

  private def glue(l: A ==>> B, r: A ==>> B): A ==>> B =
    (l, r) match {
      case (Tip(), r) => r
      case (l, Tip()) => l
      case (l @ Bin(_, _, _, _), r @ Bin(_, _, _, _)) => if (l.size > r.size) {
        val ((km, m), l2) = deleteFindMax(l)
        balanceR(km, m, l2, r)
      }
      else {
        val ((km, m), r2) = deleteFindMin(r)
        balanceL(km, m, l, r2)
      }
    }

  @tailrec
  private final def trim(lo: A => Ordering, hi: A => Ordering): A ==>> B =
    this match {
      case Tip() =>
        empty
      case t @ Bin(kx, _, l, r) =>
        lo(kx) match {
          case LT => hi(kx) match {
            case GT => t
            case _ => l.trim(lo, hi)
          }
          case _ => r.trim(lo, hi)
        }
    }

  @tailrec
  private final def trimLookupLo(lo: A, cmphi: A => Ordering)(implicit o: Order[A]): (Maybe[(A, B)], A ==>> B) =
    this match {
      case Tip() =>
        (Maybe.empty, empty)
      case t @ Bin(kx, x, l, r) =>
        o.order(lo, kx) match {
          case LT =>
            cmphi(kx) match {
              case GT =>
                (t lookupAssoc lo, t)
              case _ =>
                l.trimLookupLo(lo, cmphi)
          }
          case GT =>
            r.trimLookupLo(lo, cmphi)
          case EQ =>
            (just((kx, x)), r.trim(a => o.order(lo, a), cmphi))
        }
    }

  override final def equals(other: Any): Boolean =
    other match {
      case that: ==>>[A, B] =>
        ==>>.mapEqual[A, B](using Equal.equalA, Equal.equalA).equal(this, that)
      case _ =>
        false
    }

  override final def hashCode: Int =
    toAscList.hashCode

  // filters on keys
  private def filterGt(a: Maybe[A])(implicit o: Order[A]): A ==>> B = {
    def filter(filteringKey: A, m: A ==>> B): A ==>> B =
      m match {
        case Tip() =>
          empty
        case Bin(kx, x, l, r) =>
          o.order(filteringKey, kx) match {
            case LT => link(kx, x, filter(filteringKey, l), r)
            case EQ => r
            case GT => filter(filteringKey, r)
          }
      }

    a.cata(filter(_, this), this)
  }

  private def filterLt(a: Maybe[A])(implicit o: Order[A]): A ==>> B = {
    def filter(filteringKey: A, m: A ==>> B): A ==>> B =
      m match {
        case Tip() =>
          empty
        case Bin(kx, x, l, r) =>
          o.order(kx, filteringKey) match {
            case LT => link(kx, x, l, filter(filteringKey, r))
            case EQ => l
            case GT => filter(filteringKey, l)
          }
      }

    a.cata(filter(_, this), this)
  }

}

sealed abstract class MapInstances2 {
  implicit def mapBand[A, B](implicit A: Order[A], B: Band[B]): Band[A ==>> B] = new Band[A ==>> B] {
    def append(a: A ==>> B, b: => A ==>> B): A ==>> B =
      (a unionWith b)(B.append(_, _))
  }
}

sealed abstract class MapInstances1 extends MapInstances2 {
  implicit def mapLattice[A, B](implicit A: Order[A], B: SemiLattice[B]): SemiLattice[A ==>> B] = new SemiLattice[A ==>> B] {
    def append(a: A ==>> B, b: => A ==>> B): A ==>> B =
      (a unionWith b)(B.append(_, _))
  }
}

sealed abstract class MapInstances0 extends MapInstances1 {

  implicit def scalazMapInstance[S: Order]: Bind[==>>[S, *]] & Align[==>>[S, *]] & Zip[==>>[S, *]] =
    new Bind[==>>[S, *]] with Align[==>>[S, *]] with Zip[==>>[S, *]] {
      override def map[A, B](fa: S ==>> A)(f: A => B) =
        fa map f

      def bind[A, B](fa: S ==>> A)(f: A => (S ==>> B)) =
        fa.mapMaybeWithKey((k, v) => f(v).lookup(k))

      import \&/._, ==>>.Tip

      override def align[A, B](a: S ==>> A, b: S ==>> B) =
        (a, b) match {
          case (Tip(), Tip()) => Tip()
          case (a    , Tip()) => a.map(This(_))
          case (Tip(), b    ) => b.map(That(_))
          case (a    , b    ) =>
            a.map(This(_): A \&/ B).unionWith(b.map(That(_): A \&/ B)){
              case (This(aa), That(bb)) => Both(aa, bb)
              case _ => sys.error("==>> align")
            }
         }

      override def alignWith[A, B, C](f: A \&/ B => C) = {
        case (Tip(), Tip()) => Tip()
        case (a    , Tip()) => a.map(aa => f(This(aa)))
        case (Tip(), b    ) => b.map(bb => f(That(bb)))
        case (a    , b    ) =>
          a.map(This(_): A \&/ B).unionWith(b.map(That(_): A \&/ B)){
            case (This(aa), That(bb)) => Both(aa, bb)
            case _ => sys.error("==>> alignWith")
          }.map(f)
        }

      def zip[A, B](a: => (S ==>> A), b: => (S ==>> B)) = {
        val a0 = a
        if(a0.isEmpty) ==>>.empty
        else a0.intersectionWith(b)(Tuple2.apply)
      }

      override def zipWith[A, B, C](a: => (S ==>> A), b: => (S ==>> B))(f: (A, B) => C)(implicit F: Functor[==>>[S, *]]) = {
        val a0 = a
        if(a0.isEmpty) ==>>.empty
        else a0.intersectionWith(b)(f)
      }
    }
}

sealed abstract class MapInstances extends MapInstances0 {
  import ==>>._

  import std.tuple._
  import std.list._

  implicit def mapShow[A: Show, B: Show]: Show[==>>[A, B]] =
    Contravariant[Show].contramap(Show[List[(A, B)]])(_.toAscList)

  implicit def mapEqual[A: Equal, B: Equal]: Equal[A ==>> B] =
    new MapEqual[A, B] {def A = implicitly; def B = implicitly}

  implicit def mapOrder[A: Order, B: Order]: Order[A ==>> B] =
    new Order[A ==>> B] with MapEqual[A, B] {
      def A = implicitly
      def B = implicitly
      def order(o1: A ==>> B, o2: A ==>> B) =
        Order[List[(A,B)]].order(o1.toAscList, o2.toAscList)
    }

  implicit def mapUnion[A, B](implicit A: Order[A], B: Semigroup[B]): Monoid[A ==>> B] =
    Monoid.instance((l, r) => (l unionWith r)(B.append(_, _)), Tip())

  implicit def mapIntersection[A, B](implicit A: Order[A], B: Semigroup[B]
                                   ): Semigroup[(A ==>> B) @@ Tags.Conjunction] =
    Tag.subst(Semigroup.instance((l, r) =>
      (l intersectionWith r)(B.append(_, _))))

  implicit def mapCovariant[S]: Traverse[==>>[S, *]] =
    new Traverse[==>>[S, *]] {
      override def findLeft[A](fa: S ==>> A)(f: A => Boolean): Option[A] =
        fa match {
          case Bin(_, x, l, r) =>
            findLeft(l)(f) match {
              case a @ Some(_) =>
                a
              case None =>
                if (f(x))
                  Some(x)
                else
                  findLeft(r)(f)
            }
          case Tip() =>
            None
        }

      override def findRight[A](fa: S ==>> A)(f: A => Boolean): Option[A] =
        fa match {
          case Bin(_, x, l, r) =>
            findRight(r)(f) match {
              case a @ Some(_) =>
                a
              case None =>
                if (f(x))
                  Some(x)
                else
                  findRight(l)(f)
            }
          case Tip() =>
            None
        }

      override def map[A, B](fa: S ==>> A)(f: A => B): S ==>> B =
        fa map f

      override def foldMap[A, B](fa: S ==>> A)(f: A => B)(implicit F: Monoid[B]): B =
        fa match {
          case Tip() =>
            F.zero
          case Bin(k, x, l, r) =>
            F.append(foldMap(l)(f), F.append(f(x), foldMap(r)(f)))
        }

      override def foldRight[A, B](fa: S ==>> A, z: => B)(f: (A, => B) => B): B =
        fa.foldrWithKey(z)((_, b, acc) => f(b, acc))

      override def foldLeft[A, B](fa: S ==>> A, z: B)(f: (B, A) => B): B =
        fa.foldlWithKey(z)((acc, _, b) => f(acc, b))

      override def index[A](fa: S ==>> A, i: Int): Option[A] =
        fa.elemAt(i).map(_._2).toOption

      def traverseImpl[F[_], A, B](fa: S ==>> A)(f: A => F[B])(implicit G: Applicative[F]): F[S ==>> B] =
        fa match {
          case Tip() =>
            G.point(Tip())
          case Bin(kx, x, l, r) =>
            G.apply3(traverseImpl(l)(f), f(x), traverseImpl(r)(f)){
              (l2, x2, r2) => Bin(kx, x2, l2, r2)
            }
        }

      override def length[A](fa: S ==>> A): Int =
        fa.size

      override def any[A](fa: S ==>> A)(f: A => Boolean): Boolean =
        fa match {
          case Tip() => false
          case Bin(_, x, l, r) =>
            any(l)(f) || f(x) || any(r)(f)
        }

      override def all[A](fa: S ==>> A)(f: A => Boolean): Boolean =
        fa match {
          case Tip() => true
          case Bin(_, x, l, r) =>
            all(l)(f) && f(x) && all(r)(f)
        }

    }

  implicit val mapBifoldable: Bifoldable[==>>] = new Bifoldable[==>>] {
    def bifoldMap[A,B,M](fa: A ==>> B)(f: A => M)(g: B => M)(implicit F: Monoid[M]): M =
      fa match {
        case Tip() =>
          F.zero
        case Bin(k, x, l, r) =>
          F.append(bifoldMap(l)(f)(g),
                   F.append(f(k), F.append(g(x), bifoldMap(r)(f)(g))))
      }

    def bifoldRight[A,B,C](fa: A ==>> B, z: => C)(f: (A, => C) => C)(g: (B, => C) => C): C =
      fa.foldrWithKey(z)((a, b, c) => f(a, g(b, c)))

    override def bifoldLeft[A,B,C](fa: A ==>> B, z: C)(f: (C, A) => C)(g: (C, B) => C): C =
      fa.foldlWithKey(z)((c, a, b) => g(f(c, a), b))
  }
}

private[scalaz] sealed trait MapEqual[A, B] extends Equal[A ==>> B] {
  import std.list._
  import std.tuple._

  implicit def A: Equal[A]
  implicit def B: Equal[B]
  final override def equal(a1: A ==>> B, a2: A ==>> B): Boolean =
    Equal[Int].equal(a1.size, a2.size) && Equal[List[(A, B)]].equal(a1.toAscList, a2.toAscList)
}

object ==>> extends MapInstances {
  private[scalaz] final case class Tip[A, B] private () extends (A ==>> B) {
    val size = 0
  }
  private[scalaz] object Tip {
    private[this] val value: Tip[Nothing, Nothing] = new Tip[Nothing, Nothing]
    def apply[A, B](): A ==>> B = value.asInstanceOf[A ==>> B]
  }

  private[scalaz] final case class Bin[A, B](k: A, v: B, l: A ==>> B, r: A ==>> B) extends ==>>[A, B] {
    val size = l.size + r.size + 1
  }

  final def apply[A: Order, B](x: (A, B)*): A ==>> B =
    x.foldLeft(empty[A, B])((a, c) => a.insert(c._1, c._2))

  final def empty[A, B]: A ==>> B =
    Tip[A, B]()

  final def singleton[A, B](k: A, x: B): A ==>> B =
    Bin(k, x, Tip(), Tip())

  /* List operations */
  final def fromList[A: Order, B](l: List[(A, B)]): A ==>> B =
    l.foldLeft(empty[A, B]) { (t, x) => t.insert(x._1, x._2) }

  final def fromIList[A: Order, B](l: IList[(A, B)]): A ==>> B =
    l.foldLeft(empty[A, B]) { (t, x) => t.insert(x._1, x._2) }

  final def fromListWith[A: Order, B](l: List[(A, B)])(f: (B, B) => B): A ==>> B =
    fromListWithKey(l)((_, x, y) => f(x, y))

  final def fromIListWith[A: Order, B](l: IList[(A, B)])(f: (B, B) => B): A ==>> B =
    fromIListWithKey(l)((_, x, y) => f(x, y))

  final def fromListWithKey[A: Order, B](l: List[(A, B)])(f: (A, B, B) => B): A ==>> B =
    l.foldLeft(empty[A, B])((a, c) => a.insertWithKey(f, c._1, c._2))

  final def fromIListWithKey[A: Order, B](l: IList[(A, B)])(f: (A, B, B) => B): A ==>> B =
    l.foldLeft(empty[A, B])((a, c) => a.insertWithKey(f, c._1, c._2))

  /* TODO: Ordered lists
    , fromAscList
    , fromAscListWith
    , fromAscListWithKey
    , fromDistinctAscList
  */

  /* Foldable operations */
  final def fromFoldable[F[_]: Foldable, A: Order, B](fa: F[(A, B)]): A ==>> B =
    Foldable[F].foldLeft(fa, empty[A, B]) { (t, x) => t.insert(x._1, x._2) }

  final def fromFoldableWith[F[_]: Foldable, A: Order, B](fa: F[(A, B)])(f: (B, B) => B): A ==>> B =
    fromFoldableWithKey(fa)((_, x, y) => f(x, y))

  final def fromFoldableWithKey[F[_]: Foldable, A: Order, B](fa: F[(A, B)])(f: (A, B, B) => B): A ==>> B =
    Foldable[F].foldLeft(fa, empty[A, B])((a, c) => a.insertWithKey(f, c._1, c._2))

  final def fromSet[A: Order, B](s: ISet[A])(f: A => B): A ==>> B =
    s match {
      case ISet.Tip() =>
        empty
      case ISet.Bin(x, l, r) =>
        Bin(x, f(x), fromSet(l)(f), fromSet(r)(f))
    }

  final def unions[A: Order, B](xs: IList[A ==>> B]): A ==>> B =
    xs.foldLeft(empty[A, B])((a, c) => a.union(c))

  final def unionsWith[A: Order, B](f: (B, B) => B)(xs: IList[A ==>> B]): A ==>> B =
    xs.foldLeft(empty[A, B])((a, c) => a.unionWith(c)(f))

  private[scalaz] final val ratio = 2
  private[scalaz] final val delta = 3

  // Even though we have balanceL and balanceR, we need balance function
  // as alter function still needs it.
  private[scalaz] def balance[A, B](k: A, x: B, l: A ==>> B, r: A ==>> B): A ==>> B =
    l match {
      case Tip() =>
        r match {
          case Tip() =>
            singleton(k, x)
          case Bin(_, _, Tip(), Tip()) =>
            Bin(k, x, Tip(), r)
          case Bin(rk, rx, Tip(), rr@Bin(_, _, _, _)) =>
            Bin(rk, rx, singleton(k, x), rr)
          case Bin(rk, rx, Bin(rlk, rlx, _, _), Tip()) =>
            Bin(rlk, rlx, singleton(k, x), singleton(rk, rx))
          case Bin(rk, rx, rl@Bin(rlk, rlx, rll, rlr), rr@Bin(_, _, _, _)) =>
            if (rl.size < ratio*rr.size) Bin(rk, rx, Bin(k, x, Tip(), rl), rr)
            else Bin(rlk, rlx, Bin(k, x, Tip(), rll), Bin(rk, rx, rlr, rr))
        }
      case Bin(lk, lx, ll, lr) =>
        r match {
          case Tip() => (ll, lr) match {
            case (Tip(), Tip()) =>
              Bin(k, x, l, Tip())
            case (Tip(), Bin(lrk, lrx, _, _)) =>
              Bin(lrk, lrx, singleton(lk, lx), singleton(k, x))
            case (Bin(_, _, _, _), Tip()) =>
              Bin(lk, lx, ll, singleton(k, x))
            case (Bin(_, _, _, _), Bin(lrk, lrx, lrl, lrr)) =>
              if (lr.size < ratio*ll.size) Bin(lk, lx, ll, Bin(k, x, lr, Tip()))
              else Bin(lrk, lrx, Bin(lk, lx, ll, lrl), Bin(k, x, lrr, Tip()))
          }
          case Bin(rk, rx, rl, rr) =>
            if (r.size > delta*l.size) {
              (rl, rr) match {
                case (Bin(rlk, rlx, rll, rlr), Bin(_, _, _, _)) =>
                  if (rl.size < ratio*rr.size) Bin(rk, rx, Bin(k, x, l, rl), rr)
                  else Bin(rlk, rlx, Bin(k, x, l, rll), Bin(rk, rx, rlr, rr))
                case _ =>
                  sys.error("Failure in Map.balance")
              }
            }
            else if (l.size > delta*r.size) {
              (ll, lr) match {
                case (Bin(_, _, _, _), Bin(lrk, lrx, lrl, lrr)) =>
                  if (lr.size < ratio*ll.size) Bin(lk, lx, ll, Bin(k, x, lr, r))
                  else Bin(lrk, lrx, Bin(lk, lx, ll, lrl), Bin(k, x, lrr, r))
                case _ =>
                  sys.error("Failure in Map.balance")
              }
            }
            else Bin(k, x, l, r)
        }
    }

  // balanceL is used to balance a tree when an element might have been inserted to
  // a left subtree, or when an element might have been deleted from a right subtree.
  private[scalaz] def balanceL[A, B](k: A, x: B, l: A ==>> B, r: A ==>> B): A ==>> B =
    r match {
      case Tip() =>
        l match {
          case Tip() =>
            singleton(k, x)
          case Bin(_, _, Tip(), Tip()) =>
            Bin(k, x, l, Tip())
          case Bin(lk, lx, Tip(), Bin(lrk, lrx, _, _)) =>
            Bin(lrk, lrx, singleton(lk, lx), singleton(k, x))
          case Bin(lk, lx, ll@Bin(_, _, _, _), Tip()) =>
            Bin(lk, lx, ll, singleton(k, x))
          case Bin(lk, lx, ll@Bin(_, _, _, _), lr@Bin(lrk, lrx, lrl, lrr)) =>
            if (lr.size < ratio*ll.size) Bin(lk, lx, ll, Bin(k, x, lr, Tip()))
            else Bin(lrk, lrx, Bin(lk, lx, ll, lrl), Bin(k, x, lrr, Tip()))
        }

      case Bin(_, _, _, _) =>
        l match {
          case Tip() =>
            Bin(k, x, Tip(), r)
          case Bin(lk, lx, ll, lr) =>
            if (l.size > delta*r.size) {
              (ll, lr) match {
                case (Bin(_, _, _, _), Bin(lrk, lrx, lrl, lrr)) =>
                  if (lr.size < ratio*ll.size) Bin(lk, lx, ll, Bin(k, x, lr, r))
                  else Bin(lrk, lrx, Bin(lk, lx, ll, lrl), Bin(k, x, lrr, r))
                case _ => sys.error("Failure in Map.balanceL")
              }
            } else Bin(k, x, l, r)
        }
    }

  // balanceR is used to balance a tree when a key-value might have been inserted to
  // a right subtree, or when a key-value might have been deleted from a left subtree.
  private[scalaz] def balanceR[A, B](k: A, x: B, l: A ==>> B, r: A ==>> B): A ==>> B =
    l match {
      case Tip() =>
        r match {
          case Tip() =>
            singleton(k, x)
          case Bin(_, _, Tip(), Tip()) =>
            Bin(k, x, Tip(), r)
          case Bin(rk, rx, Tip(), rr@Bin(_, _, _, _)) =>
            Bin(rk, rx, singleton(k, x), rr)
          case Bin(rk, rx, Bin(rlk, rlx, _, _), Tip()) =>
            Bin(rlk, rlx, singleton(k, x), singleton(rk, rx))
          case Bin(rk, rx, rl@Bin(rlk, rlx, rll, rlr), rr@Bin(_, _, _, _)) =>
            if (rl.size < ratio*rr.size) Bin(rk, rx, Bin(k, x, Tip(), rl), rr)
            else Bin(rlk, rlx, Bin(k, x, Tip(), rll), Bin(rk, rx, rlr, rr))
        }
      case Bin(_, _, _, _) =>
        r match {
          case Tip() =>
            Bin(k, x, l, Tip())
          case Bin(rk, rx, rl, rr) =>
            if (r.size > delta*l.size) {
              (rl, rr) match {
                case (Bin(rlk, rlx, rll, rlr), Bin(_, _, _, _)) =>
                  if (rl.size < ratio*rr.size) Bin(rk, rx, Bin(k, x, l, rl), rr)
                  else Bin(rlk, rlx, Bin(k, x, l, rll), Bin(rk, rx, rlr, rr))
                case _ => sys.error("Failure in Map.balanceR")
              }
            } else Bin(k, x, l, r)
        }
    }

  private[scalaz] def link[A, B](kx: A, x: B, l: A ==>> B, r: A ==>> B): A ==>> B =
    (l, r) match {
      case (Tip(), r) =>
        insertMin(kx, x, r)
      case (l, Tip()) =>
        insertMax(kx, x, l)
      case (Bin(ky, y, ly, ry), Bin(kz, z, lz, rz)) =>
        if (delta * l.size < r.size) balanceL(kz, z, link(kx, x, l, lz), rz)
        else if (delta * r.size < l.size) balanceR(ky, y, ly, link(kx, x, ry, r))
        else Bin(kx, x, l, r)
    }

  // insertMax and insertMin are only used for link(...)
  private def insertMax[A, B](kx: A, x: B, t: A ==>> B): A ==>> B =
    t match {
      case Tip() =>
        singleton(kx, x)
      case Bin(ky, y, l, r) =>
        balanceR(ky, y, l, insertMax(kx, x, r))
    }

  private def insertMin[A, B](kx: A, x: B, t: A ==>> B): A ==>> B =
    t match {
      case Tip() =>
        singleton(kx, x)
      case Bin(ky, y, l, r) =>
        balanceL(ky, y, insertMin(kx, x, l), r)
    }

  private[scalaz] def trim[A, B](lo: Maybe[A], hi: Maybe[A], t: A ==>> B)(implicit o: Order[A]): A ==>> B =
    (lo, hi) match {
      case (Maybe.Empty(), Maybe.Empty()) =>
        t
      case (Just(lk), Maybe.Empty()) =>
        @tailrec
        def greater(lo: A, m: A ==>> B): A ==>> B =
          m match {
            case Bin(kx, _, _, r) if o.lessThanOrEqual(kx, lo) =>
              greater(lo, r)
            case _ =>
              m
          }
        greater(lk, t)
      case (Maybe.Empty(), Just(hk)) =>
        @tailrec
        def lesser(hi: A, m: A ==>> B): A ==>> B =
          m match {
            case Bin(kx, _, l, _) if o.greaterThanOrEqual(kx, hi) =>
              lesser(hi, l)
            case _ =>
              m
          }
        lesser(hk, t)
      case (Just(lk), Just(hk)) =>
        @tailrec
        def middle(lo: A, hi: A, m: A ==>> B): A ==>> B =
          m match {
            case Bin(kx, _, _, r) if o.lessThanOrEqual(kx, lo) =>
              middle(lo, hi, r)
            case Bin(kx, _, l, _) if o.greaterThanOrEqual(kx, hi) =>
              middle(lo, hi, l)
            case _ => m
          }
        middle(lk, hk, t)
    }

  private[scalaz] def trimLookupLo[A, B](lk: A, hkMaybe: Maybe[A], t: A ==>> B)(implicit o: Order[A]): (Maybe[B], A ==>> B) =
    hkMaybe match {
      case Empty() =>
        @tailrec
        def greater(lo: A, m: A ==>> B): (Maybe[B], A ==>> B) =
          m match {
            case Tip() =>
              (Maybe.empty, Tip())
            case Bin(kx, x, l, r) =>
              o.order(lo, kx) match {
                case LT =>
                  (l.lookup(lo), m)
                case EQ =>
                  (just(x), r)
                case GT =>
                  greater(lo, r)
              }
          }
        greater(lk, t)
      case Just(hk) =>
        @tailrec
        def middle(lo: A, hi: A, m: A ==>> B): (Maybe[B], A ==>> B) =
          m match {
            case Tip() =>
              (Maybe.empty, Tip())
            case Bin(kx, x, l, r) =>
              o.order(lo, kx) match {
                case LT if o.order(kx, hi) == LT =>
                  (l.lookup(lo), m)
                case LT =>
                  middle(lo, hi, l)
                case EQ =>
                  (just(x), lesser(hi, r))
                case GT =>
                  middle(lo, hi, r)
              }
          }
        @tailrec
        def lesser(hi: A, m: A ==>> B): A ==>> B =
          m match {
            case Bin(k, _, l, _) if o.greaterThanOrEqual(k, hi) =>
              lesser(hi, l)
            case _ =>
              m
          }
        middle(lk, hk, t)
    }

 /*
  * The documentation below is partially copied from haskell/containers Map.
  * Please refer to the full documentation if necessary.
  * @see [[https://github.com/haskell/containers/blob/v0.5.7.1/Data/Map/Base.hs#L1402-L1440]]
  *
  -- | /O(n+m)/. A high-performance universal combining function. This function
  -- is used to define 'unionWith', 'unionWithKey', 'differenceWith',
  -- 'differenceWithKey', 'intersectionWith', 'intersectionWithKey' and can be
  -- used to define other custom combine functions.

  -- When calling mergeWithKey(a, b)(f)(g1, g2), a function combining two
  -- 'Map's is created, such that
  --
  -- * if a key is present in both maps, it is passed with both corresponding
  --   values to the combine @f@ function. Depending on the result, the key is either
  --   present in the result with specified value, or is left out;
  --
  -- * a nonempty subtree present only in the first map is passed to @g1@ and
  --   the output is added to the result;
  --
  -- * a nonempty subtree present only in the second map is passed to @g2@ and
  --   the output is added to the result.
  *
  *  @param  a   first map
  *  @param  b   second map
  *  @param  f   combine function used to define `XxxWithKey`
  *  @param  g1  function applied to a nonempty subtree present only in the first map a
  *  @param  g2  function applied to a nonempty subtree present only in the second map b
  */
  def mergeWithKey[A: Order, B, C, D](a: A ==>> B, b: A ==>> C)
                                     (f: (A, B, C) => Maybe[D])
                                     (g1: (A ==>> B) => (A ==>> D), g2: (A ==>> C) => (A ==>> D))
                                     (implicit o: Order[A]): A ==>> D = {
    def hedgeMerge(blo: Maybe[A], bhi: Maybe[A], a: A ==>> B, b: A ==>> C): A ==>> D = {
      (a, b) match {
        case (t1, Tip()) => g1(t1)
        case (Tip(), Bin(kx, x, l, r)) =>
          val t2 = link(kx, x, l.filterGt(blo)(o), r.filterLt(bhi)(o))
          g2(t2)
        case (Bin(kx, x, l, r), t2) =>
          val bmi = just(kx)
          val l2 = hedgeMerge(blo, bmi, l, trim(blo, bmi, t2)(o))
          val (found, trim_t2) = trimLookupLo(kx, bhi, t2)(o)
          val r2 = hedgeMerge(bmi, bhi, r, trim_t2)
          found match {
            case Maybe.Empty() =>
              g1(singleton(kx, x)) match {
                case Tip() =>
                  l2 merge r2
                case Bin(_, x2, Tip(), Tip()) =>
                  link(kx, x2, l2, r2)
                case _ =>
                  sys.error("mergeWithKey: Given function g1 does not fulfil required conditions (see documentation)")
              }
            case Just(x2) =>
              f(kx, x, x2) match {
                case Maybe.Empty() =>
                  l2 merge r2
                case Just(xx) =>
                  link(kx, xx, l2, r2)
              }
          }
      }
    }

    (a, b) match {
      case (Tip(), t2) => g2(t2)
      case (t1, Tip()) => g1(t1)
      case (t1, t2)    => hedgeMerge(Maybe.empty, Maybe.empty, t1, t2)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy