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

scaps.nucleus.querying.ExpandedQuery.scala Maven / Gradle / Ivy

The newest version!
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package scaps.nucleus.querying

import scaps.nucleus.TypeRef
import scaps.nucleus.Variance

private[nucleus] sealed trait ExpandedQuery {
  import ExpandedQuery._

  def children: List[ExpandedQuery]

  def pretty(indent: String = ""): String =
    this match {
      case Sum(parts) => parts.map(indent + " " + _.pretty(indent + " ")).mkString(s"${indent}sum(\n", ",\n", ")")
      case Max(alts)  => alts.map(indent + " " + _.pretty(indent + " ")).mkString(s"${indent}max(\n", ",\n", ")")
      case l: Leaf    => indent + l.toString()
    }

  def minimize(): ExpandedQuery = {
    val min = this match {
      case a: Alternative => ExpandedQuery.minimize(a)
      case p: Part        => ExpandedQuery.minimize(p)
    }

    if (min == this)
      min
    else
      min.minimize()
  }
}

private[nucleus] object ExpandedQuery {
  sealed trait Part extends ExpandedQuery
  sealed trait Alternative extends ExpandedQuery

  case class Sum(parts: List[Part]) extends Alternative {
    val children = parts

    override def toString =
      parts.mkString("sum(", ", ", ")")
  }
  object Sum {
    def apply(parts: Part*): Sum =
      Sum(parts.toList)
  }

  case class Max(alternatives: List[Alternative]) extends Part {
    val children = alternatives

    override def toString =
      alternatives.mkString("max(", ", ", ")")
  }
  object Max {
    def apply(alts: Alternative*): Max =
      Max(alts.toList)
  }

  case class Leaf(variance: Variance, name: String, fraction: Double, depth: Int, dist: Float) extends Part with Alternative {
    val children = Nil

    override def toString =
      s"${variance.prefix}$name^($fraction, $depth, $dist)"
  }

  def minimize(p: Part): Part =
    p match {
      case Max((alt: Leaf) :: Nil)   => alt
      case Max(Sum(p :: Nil) :: Nil) => p
      case Max(alts) =>
        val minAlts = alts.map(minimize)

        maxRepeatedPart(minAlts).fold[Part] {
          Max(minAlts)
        } { part =>
          minimize(Max(factorOut(part, minAlts)))
        }
      case _ => p
    }

  private def maxRepeatedPart(alts: List[Alternative]): Option[Part] = {
    alts
      .flatMap {
        case Sum(parts) => parts.distinct
        case _          => Nil
      }
      .groupBy(identity)
      .mapValues(_.length)
      .filter(_._2 > 1)
      .maxByOpt(_._2)
      .map(_._1)
  }

  private def factorOut(part: Part, alts: List[Alternative]): List[Alternative] = {
    val (altsWithPart, altsWithoutPart) = alts.partition {
      case Sum(ps) => ps.contains(part)
      case _       => false
    }

    val altsMinusPart = altsWithPart.map {
      case Sum(ps) => Sum(ps diff List(part))
      case _       => ???
    }

    Sum(Max(altsMinusPart) :: part :: Nil) :: altsWithoutPart
  }

  def minimize(a: Alternative): Alternative = a match {
    case Sum((part: Leaf) :: Nil)  => part
    case Sum(Max(a :: Nil) :: Nil) => a
    case Sum(parts)                => Sum(parts.map(minimize))
    case _                         => a
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy