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

cats.kernel.Group.scala Maven / Gradle / Ivy

The newest version!
package cats.kernel

import scala.{specialized => sp}

/**
 * A group is a monoid where each element has an inverse.
 */
trait Group[@sp(Int, Long, Float, Double) A] extends Any with Monoid[A] {

  /**
   * Find the inverse of `a`.
   *
   * `combine(a, inverse(a))` = `combine(inverse(a), a)` = `empty`.
   *
   * Example:
   * {{{
   * scala> import cats.kernel.instances.int._
   *
   * scala> Group[Int].inverse(5)
   * res0: Int = -5
   * }}}
   */
  def inverse(a: A): A

  /**
   * Remove the element `b` from `a`.
   *
   * Equivalent to `combine(a, inverse(b))`
   *
   * Example:
   * {{{
   * scala> import cats.kernel.instances.int._
   *
   * scala> Group[Int].remove(5, 2)
   * res0: Int = 3
   * }}}
   */
  def remove(a: A, b: A): A = combine(a, inverse(b))

  /**
   * Return `a` appended to itself `n` times. If `n` is negative, then
   * this returns `inverse(a)` appended to itself `n` times.
   */
  override def combineN(a: A, n: Int): A =
    // This method is a bit tricky. Normally, to sum x a negative
    // number of times (n), we can sum (-x) a positive number of times
    // (-n). The issue here is that Int.MinValue cannot be negated; in
    // other words (-MinValue) == MinValue.
    //
    // To work around that, we rely on the fact that we can divide n
    // by 2 if we sum 2a (i.e. combine(a, a)) instead. Here is the
    // transformation we use:
    //
    //   combineN(x, -2147483648)
    //   combineN(combine(x, x), -1073741824)
    //   combineN(inverse(combine(x, x)), 1073741824)
    if (n > 0) repeatedCombineN(a, n)
    else if (n == 0) empty
    else if (n == Int.MinValue) combineN(inverse(combine(a, a)), 1073741824)
    else repeatedCombineN(inverse(a), -n)
}

abstract class GroupFunctions[G[T] <: Group[T]] extends MonoidFunctions[Group] {
  def inverse[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: G[A]): A =
    ev.inverse(a)

  def remove[@sp(Int, Long, Float, Double) A](x: A, y: A)(implicit ev: G[A]): A =
    ev.remove(x, y)
}

object Group extends GroupFunctions[Group] {

  /**
   * Access an implicit `Group[A]`.
   */
  @inline final def apply[A](implicit ev: Group[A]): Group[A] = ev
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy