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

com.rklaehn.abc.TotalArrayMap.scala Maven / Gradle / Ivy

The newest version!
package com.rklaehn.abc

import algebra._
import algebra.ring._
import cats.Show
import cats.syntax.show._

final class TotalArrayMap[@sp(ILD) K, @sp(ILD) V](
    private[abc] val keys0: Array[K],
    private[abc] val values0: Array[V],
    val default: V
  ) { lhs ⇒

  import TotalArrayMap._

  def withoutDefault: ArrayMap[K, V] = new ArrayMap[K, V](keys0, values0)

  def iterator = keys0.iterator zip values0.iterator

  def size: Int = keys0.length

  def keys: ArraySet[K] = new ArraySet[K](keys0)

  def values: ArraySeq[V] = new ArraySeq[V](values0)

  def apply(k: K)(implicit K: Order[K]): V = {
    val i = Searching.search(keys0, 0, keys0.length, k)
    if (i >= 0) values0(i)
    else default
  }

  def combine(rhs: TotalArrayMap[K, V], f: (V, V) ⇒ V)(implicit kOrder: Order[K], vEq: Eq[V]): TotalArrayMap[K, V] =
    new Combine[K, V](lhs, rhs, f(lhs.default, rhs.default), f).result

  def mapValues[@sp(ILD) W](f: V ⇒ W)(implicit wEq: Eq[W], wClassTag: ClassTag[W]): TotalArrayMap[K, W] = {
    val rk = newArray[K](size, keys0)
    val rv = new Array[W](size)
    val rd = f(default)
    var i = 0
    var ri = 0
    while(i < keys0.length) {
      val r = f(values0(i))
      if(Eq.neqv(r, rd)) {
        rk(ri) = keys0(i)
        rv(ri) = r
        ri += 1
      }
      i += 1
    }
    val keys1 = if(ri == keys0.length) keys0 else rk.resizeInPlace(ri)
    val values1 = rv.resizeInPlace(ri)
    new TotalArrayMap[K, W](keys1, values1, rd)
  }

  private[abc] def mapValues0(f: V ⇒ V)(implicit V: Eq[V]): TotalArrayMap[K, V] = {
    val rk = newArray[K](size, keys0)
    val rv = newArray[V](size, values0)
    val rd = f(default)
    var i = 0
    var ri = 0
    while(i < keys0.length) {
      val r = f(values0(i))
      if(Eq.neqv(r, rd)) {
        rk(ri) = keys0(i)
        rv(ri) = r
        ri += 1
      }
      i += 1
    }
    val keys1 = if(ri == keys0.length) keys0 else rk.resizeInPlace(ri)
    val values1 = rv.resizeInPlace(ri)
    new TotalArrayMap[K, V](keys1, values1, rd)
  }

  override def equals(that: Any): Boolean = that match {
    case that: TotalArrayMap[K, V] => TotalArrayMap.eqv(Universal[K], Universal[V]).eqv(this, that)
    case _ => false
  }

  override def hashCode(): Int = TotalArrayMap.hash(Universal[K], Universal[V]).hash(this)

  override def toString: String = TotalArrayMap.show(Universal[K], Universal[V]).show(this)
}

private[abc] trait TotalArrayMap1 {

  implicit def eqv[K: Eq, V: Eq]: Eq[TotalArrayMap[K, V]] = new Eq[TotalArrayMap[K, V]] {
    def eqv(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]) =
      Eq.eqv(x.default, y.default) && ArrayUtil.eqv(x.keys0, y.keys0) && ArrayUtil.eqv(x.values0, y.values0)
  }
}

private[abc] trait TotalArrayMap2 extends TotalArrayMap1 {
  import TotalArrayMap._

  implicit def order[K: Order, V: Order]: Order[TotalArrayMap[K, V]] = new Order[TotalArrayMap[K, V]] {
    override def eqv(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]) =
      Eq.eqv(x.default, y.default) && ArrayUtil.eqv(x.keys0, y.keys0) && ArrayUtil.eqv(x.values0, y.values0)

    def compare(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]) =
      TotalArrayMap.compare(x, y)
  }

  implicit def monoid[K : Order: ClassTag, V: Monoid: Eq: ClassTag]: Monoid[TotalArrayMap[K, V]] =
    new ArrayTotalMapMonoid[K, V]

  implicit def additiveMonoid[K: Order: ClassTag, V: AdditiveMonoid: Eq: ClassTag]: AdditiveMonoid[TotalArrayMap[K, V]] =
    new ArrayTotalMapAdditiveMonoid[K, V]

  implicit def multiplicativeMonoid[K: Order: ClassTag, V: MultiplicativeMonoid: Eq: ClassTag]: MultiplicativeMonoid[TotalArrayMap[K, V]] =
    new ArrayTotalMapMultiplicativeMonoid[K, V]
}

private[abc] trait TotalArrayMap3 extends TotalArrayMap2 {
  import TotalArrayMap._

  implicit def group[K: Order: ClassTag, V: Group: Eq: ClassTag]: Group[TotalArrayMap[K, V]] =
    new ArrayTotalMapGroup[K, V]

  implicit def additiveGroup[K: Order: ClassTag, V: AdditiveGroup: Eq: ClassTag]: AdditiveGroup[TotalArrayMap[K, V]] =
    new ArrayTotalMapAdditiveGroup[K, V]

  implicit def multiplicativeGroup[K: Order: ClassTag, V: MultiplicativeGroup: Eq: ClassTag]: MultiplicativeGroup[TotalArrayMap[K, V]] =
    new ArrayTotalMapMultiplicativeGroup[K, V]

  implicit def semiring[K: Order: ClassTag, V: Semiring: Eq: ClassTag]: Semiring[TotalArrayMap[K, V]] =
    new ArrayTotalMapSemiring[K, V]
}

object TotalArrayMap extends TotalArrayMap3 {

  private def fastCombine[@sp(ILD) K: Order, @sp(ILD) V: Eq](lhs: TotalArrayMap[K,V], rhs: TotalArrayMap[K, V], f: (V, V) ⇒ V): TotalArrayMap[K, V] =
    new FastCombine[K, V](lhs, rhs, lhs.default, f).result

  implicit def rng[K: Order: ClassTag, V: Rng: Eq: ClassTag]: Rng[TotalArrayMap[K, V]] =
    new TotalArrayMapRng[K, V]

  private[abc] class ArrayTotalMapMonoid[K : Order: ClassTag, V: Monoid: Eq: ClassTag]
    extends Monoid[TotalArrayMap[K, V]] {
    override def combine(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] = {
      val m = Monoid[V]
      if(m.isEmpty(x.default) && m.isEmpty(y.default))
        fastCombine(x, y, m.combine)
      else
        x.combine(y, m.combine)
    }
    override def empty: TotalArrayMap[K, V] = TotalArrayMap.fromDefault[K, V](Monoid[V].empty)
  }

  private[abc] final class ArrayTotalMapGroup[K: Order: ClassTag, V: Group: Eq: ClassTag]
    extends ArrayTotalMapMonoid[K, V] with Group[TotalArrayMap[K, V]] {
    override def inverse(x: TotalArrayMap[K, V]): TotalArrayMap[K, V] = x.mapValues0(x ⇒ Group.inverse(x))
    override def remove(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] =
      x.combine(y, (x,y) ⇒ Group.remove(x, y))
  }

  private[abc] class ArrayTotalMapAdditiveMonoid[K: Order: ClassTag, V: AdditiveMonoid: Eq: ClassTag]
    extends AdditiveMonoid[TotalArrayMap[K, V]] {
    override def zero: TotalArrayMap[K, V] = TotalArrayMap.fromDefault[K, V](AdditiveMonoid[V].zero)
    override def plus(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] = {
      val m = AdditiveMonoid[V]
      if(m.isZero(x.default) && m.isZero(y.default))
        fastCombine(x, y, m.plus)
      else
        x.combine(y, m.plus)
    }
  }

  private[abc] final class ArrayTotalMapAdditiveGroup[K: Order: ClassTag, V: AdditiveGroup: Eq: ClassTag]
    extends ArrayTotalMapAdditiveMonoid[K, V] with AdditiveGroup[TotalArrayMap[K, V]] {
    override def negate(x: TotalArrayMap[K, V]): TotalArrayMap[K, V] = x.mapValues0(x ⇒ AdditiveGroup.negate(x))
    override def minus(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] =
      x.combine(y, (x,y) ⇒ AdditiveGroup.minus(x, y))
  }

  private[abc] class ArrayTotalMapMultiplicativeMonoid[K: Order: ClassTag, V: MultiplicativeMonoid: Eq: ClassTag]
    extends MultiplicativeMonoid[TotalArrayMap[K, V]] {
    override def one: TotalArrayMap[K, V] = TotalArrayMap.fromDefault[K, V](MultiplicativeMonoid[V].one)
    override def times(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] = {
      val m = MultiplicativeMonoid[V]
      if(m.isOne(x.default) && m.isOne(y.default))
        fastCombine(x, y, m.times)
      else
        x.combine(y, m.times)
    }
  }

  private[abc] final class ArrayTotalMapMultiplicativeGroup[K: Order: ClassTag, V: MultiplicativeGroup: Eq: ClassTag]
    extends ArrayTotalMapMultiplicativeMonoid[K, V] with MultiplicativeGroup[TotalArrayMap[K, V]] {
    override def reciprocal(x: TotalArrayMap[K, V]): TotalArrayMap[K, V] = x.mapValues0(x ⇒ MultiplicativeGroup.reciprocal(x))
    override def div(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] =
      x.combine(y, (x,y) ⇒ MultiplicativeGroup.div(x, y))
  }

  private[abc] class ArrayTotalMapSemiring[K: Order: ClassTag, V: Semiring: Eq: ClassTag]
    extends Semiring[TotalArrayMap[K, V]] {
    def plus(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] = {
      val m = AdditiveCommutativeMonoid[V]
      if(m.isZero(x.default) && m.isZero(y.default))
        fastCombine(x, y, m.plus)
      else
        x.combine(y, m.plus)
    }
    def times(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): TotalArrayMap[K, V] =  {
      val m = MultiplicativeSemigroup[V]
      x.combine(y, m.times)
    }
    def zero = TotalArrayMap.fromDefault[K, V](AdditiveCommutativeMonoid[V].zero)
  }

  private[abc] class TotalArrayMapRng[K: Order: ClassTag, V: Rng: Eq: ClassTag]
    extends ArrayTotalMapSemiring[K, V] with Rng[TotalArrayMap[K, V]] {
    def negate(x: TotalArrayMap[K, V]) = x.mapValues0(x ⇒ Rng.negate(x))
    override def minus(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]) =
      x.combine(y, (x, y) ⇒ Rng.minus(x, y))
  }

  implicit def show[K: Show, V: Show]: Show[TotalArrayMap[K, V]] = Show.show(m ⇒
    m.iterator
      .map { case (k,v) ⇒ s"${k.show}->${v.show}" }
      .mkString(s"TotalArrayMap((", ",", s"), ${m.default.show})")
  )

  implicit def hash[K: Hash, V: Hash]: Hash[TotalArrayMap[K, V]] = new Hash[TotalArrayMap[K, V]] {
    def hash(x: TotalArrayMap[K, V]) =
      (Hash.hash(x.default), Hash.hash(x.keys0), Hash.hash(x.values0)).##

    def eqv(x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]) =
      Eq.eqv(x.default, y.default) && ArrayUtil.eqv(x.keys0, y.keys0) && ArrayUtil.eqv(x.values0, y.values0)
  }

  private final class Combine[@sp(ILD) K: Order, @sp(ILD) V: Eq](
    a: TotalArrayMap[K, V], b: TotalArrayMap[K, V], rd: V, f: (V, V) => V)
    extends BinaryMerge {

    @inline def ak = a.keys0
    @inline def av = a.values0
    @inline def bk = b.keys0
    @inline def bv = b.values0

    val rk = newArray[K](a.size + b.size, ak)
    val rv = newArray[V](a.size + b.size, av)
    var ri = 0

    def compare(ai: Int, bi: Int) = Order.compare(ak(ai), bk(bi))

    def fromA(a0: Int, a1: Int, bi: Int) = {
      var ai = a0
      while(ai < a1) {
        val r = f(av(ai), b.default)
        if(Eq.neqv(r, rd)) {
          rk(ri) = ak(ai)
          rv(ri) = r
          ri += 1
        }
        ai += 1
      }
    }

    def fromB(ai: Int, b0: Int, b1: Int) = {
      var bi = b0
      while(bi < b1) {
        val r = f(a.default, bv(bi))
        if(Eq.neqv(r, rd)) {
          rk(ri) = bk(bi)
          rv(ri) = r
          ri += 1
        }
        bi += 1
      }
    }

    def collision(ai: Int, bi: Int) = {
      val r = f(av(ai), bv(bi))
      if(Eq.neqv(r, rd)) {
        rk(ri) = bk(bi)
        rv(ri) = r
        ri += 1
      }
    }

    merge0(0, ak.length, 0, bk.length)

    def result: TotalArrayMap[K, V] = new TotalArrayMap[K, V](rk.resizeInPlace(ri), rv.resizeInPlace(ri), rd)
  }

  // fast path for combine in case a.default, b.default and r.default is the neutral element
  // in this case we know that we can just copy over elements in a but not in b, or in b but not in a
  // this is especially important when combining a small and a large map
  //
  // for collisions we still have to check if the result is the default. E.g. when adding Int.MinValue + Int.MinValue,
  // you get back 0, the neutral element of addition
  private final class FastCombine[@sp(ILD) K: Order, @sp(ILD) V: Eq](
    a: TotalArrayMap[K, V], b: TotalArrayMap[K, V], rd: V, f: (V, V) => V)
    extends BinaryMerge {

    @inline def ak = a.keys0
    @inline def av = a.values0
    @inline def bk = b.keys0
    @inline def bv = b.values0

    val rk = newArray[K](a.size + b.size, ak)
    val rv = newArray[V](a.size + b.size, av)
    var ri = 0

    def compare(ai: Int, bi: Int) = Order.compare(ak(ai), bk(bi))

    def fromA(a0: Int, a1: Int, bi: Int) = {
      System.arraycopy(ak, a0, rk, ri, a1 - a0)
      System.arraycopy(av, a0, rv, ri, a1 - a0)
      ri += a1 - a0
    }

    def fromB(ai: Int, b0: Int, b1: Int) = {
      System.arraycopy(bk, b0, rk, ri, b1 - b0)
      System.arraycopy(bv, b0, rv, ri, b1 - b0)
      ri += b1 - b0
    }

    def collision(ai: Int, bi: Int) = {
      val r = f(av(ai), bv(bi))
      if(Eq.neqv(r, rd)) {
        rk(ri) = bk(bi)
        rv(ri) = r
        ri += 1
      }
    }

    merge0(0, ak.length, 0, bk.length)

    def result: TotalArrayMap[K, V] = new TotalArrayMap[K, V](rk.resizeInPlace(ri), rv.resizeInPlace(ri), rd)
  }

  private[abc] def compare[@sp(ILD) K: Order, @sp(ILD) V: Order](x: TotalArrayMap[K, V], y: TotalArrayMap[K, V]): Int =
    new Compare[K, V](x, y).merge()

  private final class Compare[@sp(ILD) K: Order, @sp(ILD) V: Order](a: TotalArrayMap[K, V], b: TotalArrayMap[K, V])
    extends BinaryMerge {
    // require(Eq.eqv(a.default, b.default))

    @inline def ak = a.keys0
    @inline def av = a.values0
    @inline def bk = b.keys0
    @inline def bv = b.values0
    var r: Int = 0

    def compare(ai: Int, bi: Int) = Order.compare(ak(ai), bk(bi))

    def fromA(a0: Int, a1: Int, bi: Int) = {
      // merge0 only gets called if a.default === b.default
      // av(a0) must be =!= a.default and thus =!= b.default
      // so this comparison will never return 0, and we can immediately abort
      r = Order.compare(av(a0), b.default)
      throw abort
    }

    def fromB(ai: Int, b0: Int, b1: Int) = {
      // merge0 only gets called if a.default === b.default
      // bv(b0) must be =!= b.default and thus =!= a.default
      // so this comparison will never return 0, and we can immediately abort
      r = Order.compare(a.default, bv(b0))
      throw abort
    }

    def collision(ai: Int, bi: Int) = {
      r = Order.compare(av(ai), bv(bi))
      if(r != 0)
        throw abort
    }

    def merge(): Int = {
      r = Order.compare(a.default, b.default)
      if (r == 0)
        try {
          merge0(0, ak.length, 0, bk.length)
        } catch {
          case x: AbortControl ⇒
        }
      r
    }
  }

  def fromDefault[@sp(ILD) K: ClassTag, @sp(ILD) V: ClassTag](default: V): TotalArrayMap[K, V] =
    new TotalArrayMap(Array.empty[K], Array.empty[V], default)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy