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

org.alephium.util.Forest.scala Maven / Gradle / Ivy

// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .

package org.alephium.util

import scala.collection.mutable

import org.alephium.util.Forest.Node

object Forest {
  // Note: the parent node should comes first in values; otherwise return None
  def tryBuild[K, T](values: AVector[T], toKey: T => K, toParent: T => K): Option[Forest[K, T]] = {
    val rootParents = mutable.HashMap.empty[K, mutable.ArrayBuffer[Node[K, T]]]
    val nodes       = mutable.HashMap.empty[K, Node[K, T]]

    import org.alephium.macros.HPC.cfor
    cfor(0)(_ < values.length, _ + 1) { i =>
      val value = values(i)
      val key   = toKey(value)
      if (rootParents.contains(key)) {
        // scalastyle:off return
        return None
        // scalastyle:on return
      } else {
        val node      = Node(key, value, mutable.ArrayBuffer.empty[Node[K, T]])
        val parentKey = toParent(value)
        rootParents.get(parentKey) match {
          case Some(children) => children.append(node)
          case None =>
            nodes.get(parentKey) match {
              case Some(parentNode) => parentNode.children.append(node)
              case None             => rootParents += (parentKey -> mutable.ArrayBuffer(node))
            }
        }
        nodes += key -> node
      }
    }

    val roots = mutable.ArrayBuffer.empty[Node[K, T]]
    rootParents.values.foreach(roots ++= _)
    Some(new Forest(roots))
  }

  def build[K, T](value: T, toKey: T => K): Forest[K, T] = {
    val node = Node(toKey(value), value, mutable.ArrayBuffer.empty[Node[K, T]])
    new Forest(mutable.ArrayBuffer(node))
  }

  final case class Node[K, T](key: K, value: T, children: mutable.ArrayBuffer[Node[K, T]]) {
    @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
    def contains(another: K): Boolean = {
      key == another || children.exists(_.contains(another))
    }

    @SuppressWarnings(Array("org.wartremover.warts.Recursion"))
    def flatten: AVector[Node[K, T]] = {
      AVector(this) ++ children.foldLeft(AVector.empty[Node[K, T]])(_ ++ _.flatten)
    }
  }
}

// Note: we use ArrayBuffer instead of Set because the number of forks in blockchain is usually small
final class Forest[K, T](val roots: mutable.ArrayBuffer[Node[K, T]]) {
  def isEmpty: Boolean = roots.isEmpty

  def nonEmpty: Boolean = roots.nonEmpty

  def contains(key: K): Boolean = {
    roots.exists(_.contains(key))
  }

  def flatten: AVector[Node[K, T]] = {
    roots.foldLeft(AVector.empty[Node[K, T]])(_ ++ _.flatten)
  }

  def removeRootNode(key: K): Option[Node[K, T]] = {
    withRemove(key) { (index, node) =>
      roots.remove(index)
      roots.appendAll(node.children)
    }
  }

  def removeBranch(key: K): Option[Node[K, T]] = {
    withRemove(key) { (index, _) =>
      roots.remove(index)
      ()
    }
  }

  private def withRemove(key: K)(f: (Int, Node[K, T]) => Unit): Option[Node[K, T]] = {
    val index = roots.indexWhere(_.key == key)
    if (index == -1) {
      None
    } else {
      val node = roots(index)
      f(index, node)
      Some(node)
    }
  }

  // Note: the other forest might connected to this current forest
  def simpleMerge(another: Forest[K, T]): Unit = {
    roots.appendAll(another.roots)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy