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

swaydb.core.util.MinMax.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020 Simer JS Plaha ([email protected] - @simerplaha)
 *
 * This file is a part of SwayDB.
 *
 * SwayDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * SwayDB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with SwayDB. If not, see .
 *
 * Additional permission under the GNU Affero GPL version 3 section 7:
 * If you modify this Program or any covered work, only by linking or combining
 * it with separate works, the licensors of this Program grant you additional
 * permission to convey the resulting work.
 */

package swaydb.core.util

import swaydb.core.data.Value.{FromValue, FromValueOption}
import swaydb.core.data.{KeyValue, Memory, Value}
import swaydb.core.function.FunctionStore
import swaydb.data.slice.Slice
import swaydb.data.util.{SomeOrNone, SomeOrNoneCovariant}

import scala.annotation.tailrec

private[core] object MinMax {

  implicit class MinMaxByteImplicits(minMax: MinMax[Slice[Byte]]) {
    def unslice(): MinMax[Slice[Byte]] =
      MinMax(
        min = minMax.min.unslice(),
        max = minMax.max.unslice()
      )
  }

  private def minimum[T](left: T,
                         right: T)(implicit ordering: Ordering[T]): T =
    if (ordering.compare(left, right) <= 0)
      left
    else
      right

  private def maximum[T](left: T,
                         right: T)(implicit ordering: Ordering[T]): T =
    if (ordering.compare(left, right) >= 0)
      left
    else
      right

  private def pickOne[T](someKey1: Option[T],
                         someKey2: Option[T],
                         condition: (T, T) => T): Option[T] =
    someKey1 match {
      case Some(key1) =>
        someKey2 match {
          case Some(key2) =>
            Some(condition(key1, key2))

          case None =>
            someKey1
        }

      case None =>
        someKey2
    }

  /**
   * Picks the smallest of the two. Favours left if equal.
   */
  def minFavourLeft[T](left: Option[T],
                       right: Option[T])(implicit ordering: Ordering[T]): Option[T] =
    pickOne[T](left, right, minimum)

  /**
   * Picks the largest of the two. Favours left if equal.
   */
  def maxFavourLeft[T](left: Option[T],
                       right: Option[T])(implicit ordering: Ordering[T]): Option[T] =
    pickOne[T](left, right, maximum)

  def maxFavourLeft[T](left: T,
                       right: Option[T])(implicit ordering: Ordering[T]): T =
    right match {
      case Some(right) =>
        maximum[T](left, right)

      case None =>
        left
    }

  def minFavourLeft[T](left: T,
                       right: T)(implicit ordering: Ordering[T]): T =
    minimum[T](left, right)

  def maxFavourLeft[T](left: T,
                       right: T)(implicit ordering: Ordering[T]): T =
    maximum[T](left, right)

  def maxFavourLeftC[T <: SomeOrNoneCovariant[T, SOME], SOME <: T](left: T,
                                                                   right: T)(implicit ordering: Ordering[SOME]): T =
    if (left.isNoneC)
      right
    else if (right.isNoneC)
      left
    else
      maximum(left.getC, right.getC)

  def minFavourLeftC[T <: SomeOrNoneCovariant[T, SOME], SOME <: T](left: T,
                                                                   right: T)(implicit ordering: Ordering[SOME]): T =
    if (left.isNoneC)
      right
    else if (right.isNoneC)
      left
    else
      minimum(left.getC, right.getC)

  def maxFavourLeftS[T <: SomeOrNone[T, SOME], SOME <: T](left: T,
                                                          right: T)(implicit ordering: Ordering[SOME]): T =
    if (left.isNoneS)
      right
    else if (right.isNoneS)
      left
    else
      maximum(left.getS, right.getS)

  def minFavourLeftS[T <: SomeOrNone[T, SOME], SOME <: T](left: T,
                                                          right: T)(implicit ordering: Ordering[SOME]): T =
    if (left.isNoneS)
      right
    else if (right.isNoneS)
      left
    else
      minimum(left.getS, right.getS)

  def maxFavourLeft[T](left: Option[T],
                       right: T)(implicit ordering: Ordering[T]): T =
    left match {
      case Some(left) =>
        maximum[T](left, right)

      case None =>
        right
    }

  def contains[T](key: T, minMax: MinMax[T])(implicit order: Ordering[T]): Boolean =
    minMax.max match {
      case Some(maxValue) =>
        import order._
        key >= minMax.min && key <= maxValue

      case None =>
        order.equiv(key, minMax.min)
    }

  def minMaxFunction(function: Option[Value],
                     current: Option[MinMax[Slice[Byte]]]): Option[MinMax[Slice[Byte]]] =
    function flatMap {
      function =>
        minMaxFunction(
          function = function,
          current = current
        )
    } orElse current

  def minMaxFunction(function: Value,
                     current: Option[MinMax[Slice[Byte]]]): Option[MinMax[Slice[Byte]]] =
    function match {
      case _: Value.Remove | _: Value.Update | _: Value.Put =>
        current

      case function: Value.Function =>
        Some(
          minMaxFunction(
            function = function,
            current = current
          )
        )

      case Value.PendingApply(applies) =>
        minMaxFunction(
          functions = applies,
          current = current
        )
    }

  def minMaxFunction(function: Value.Function,
                     current: Option[MinMax[Slice[Byte]]]): MinMax[Slice[Byte]] =
    minMax(
      current = current,
      next = function.function
    )(FunctionStore.order)

  def minMaxFunction(function: Memory.Function,
                     current: Option[MinMax[Slice[Byte]]]): MinMax[Slice[Byte]] =
    minMax(
      current = current,
      next = function.function
    )(FunctionStore.order)

  def minMaxFunction(function: KeyValue.Function,
                     current: Option[MinMax[Slice[Byte]]]): MinMax[Slice[Byte]] =
    minMax(
      current = current,
      next = function.getOrFetchFunction
    )(FunctionStore.order)

  def minMaxFunction(range: Memory.Range,
                     current: Option[MinMax[Slice[Byte]]]): Option[MinMax[Slice[Byte]]] =
    minMaxFunction(
      fromValue = range.fromValue,
      rangeValue = range.rangeValue,
      current = current
    )

  def minMaxFunction(fromValue: FromValueOption,
                     rangeValue: Value.RangeValue,
                     current: Option[MinMax[Slice[Byte]]]): Option[MinMax[Slice[Byte]]] =
    minMaxFunction(
      function = rangeValue,
      current =
        fromValue match {
          case FromValue.Null =>
            current

          case fromValue: Value.FromValue =>
            minMaxFunction(
              function = fromValue,
              current = current
            )
        }
    )

  def minMaxFunction(range: KeyValue.Range,
                     current: Option[MinMax[Slice[Byte]]]): Option[MinMax[Slice[Byte]]] = {
    val (fromValue, rangeValue) = range.fetchFromAndRangeValueUnsafe
    minMaxFunction(
      fromValue = fromValue,
      rangeValue = rangeValue,
      current = current
    )
  }

  @tailrec
  def minMaxFunction(functions: Slice[Value],
                     current: Option[MinMax[Slice[Byte]]]): Option[MinMax[Slice[Byte]]] =
    functions.headOption match {
      case Some(function) =>
        minMaxFunction(
          functions = functions.dropHead(),
          current =
            minMaxFunction(
              function = function,
              current = current
            )
        )

      case None =>
        current
    }

  def minMax[T](left: Option[MinMax[T]],
                right: Option[MinMax[T]])(implicit order: Ordering[T]): Option[MinMax[T]] =
    (left, right) match {
      case (Some(left), Some(right)) =>
        val minMaxLeft = MinMax.minMax(left, right.min)
        val minMaxRight = right.max.map(MinMax.minMax(minMaxLeft, _))
        minMaxRight orElse Some(minMaxLeft)

      case (None, right @ Some(_)) =>
        right

      case (left @ Some(_), None) =>
        left

      case (None, None) =>
        None
    }

  def minMax[T](current: Option[MinMax[T]],
                next: T)(implicit order: Ordering[T]): MinMax[T] =
    current match {
      case Some(currentMinMax) =>
        minMax(currentMinMax, next)

      case None =>
        MinMax(min = next, None)
    }

  def minMax[T](current: MinMax[T],
                next: T)(implicit order: Ordering[T]): MinMax[T] = {
    import order._
    val minCompare = order.compare(next, current.min)
    if (minCompare == 0)
      MinMax(
        min = next,
        max = current.max
      )
    else if (minCompare < 0)
      MinMax(
        min = next,
        max = current.max orElse Some(current.min)
      )
    else if (current.max.forall(_ <= next))
      current.copy(max = Some(next))
    else
      current
  }
}

private[core] case class MinMax[T](min: T,
                                   max: Option[T])




© 2015 - 2025 Weber Informatics LLC | Privacy Policy