blob: 6e00f14636c89ed731d45f3f0bfc2b8be98a04d3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.polaris.benchmarks
/**
* Builds a complete N-ary tree structure for organizing namespaces in synthetic datasets.
*
* This builder is used to create a hierarchical namespace structure where each non-leaf namespace
* has exactly `nsWidth` child namespaces. The tree structure is used to organize tables and views
* in a deterministic way. Tables and views are placed in the leaf namespaces of the tree.
*
* @param nsWidth The number of children each non-leaf namespace will have (N)
* @param nsDepth The total depth of the tree, including the root namespace
*/
case class NAryTreeBuilder(nsWidth: Int, nsDepth: Int) {
/**
* Computes the path from the root node to the given ordinal.
*
* @param ordinal the ordinal of the node
* @param acc the accumulator for the path
* @return the path from the root node (included) to the given ordinal (included)
*/
@scala.annotation.tailrec
final def pathToRoot(ordinal: Int, acc: List[Int] = Nil): List[Int] =
if (ordinal == 0) {
ordinal :: acc
} else {
val parent = (ordinal - 1) / nsWidth
pathToRoot(parent, ordinal :: acc)
}
/**
* Computes the ordinals of all child nodes for a given node.
*
* @param ordinal the ordinal of the parent node
* @return a list of ordinals representing the child nodes
*/
def childrenOf(ordinal: Int): List[Int] =
if (depthOf(ordinal) >= nsDepth - 1) {
// Node is a leaf, has no children
List.empty
} else {
// For a node with ordinal p, its children have ordinals: p*nsWidth + 1, p*nsWidth + 2, ..., p*nsWidth + nsWidth
val firstChild = ordinal * nsWidth + 1
(firstChild until firstChild + nsWidth).toList
}
/**
* Calculates the depth of a node in the n-ary tree based on its ordinal.
*
* @param ordinal The ordinal of the node.
* @return The depth of the node in the tree.
*/
def depthOf(ordinal: Int): Int = {
if (ordinal == 0) return 0
if (nsWidth == 1) return ordinal
// Using the formula: floor(log_n((x * (n-1)) + 1))
val numerator = (ordinal * (nsWidth - 1)) + 1
(math.log(numerator) / math.log(nsWidth)).floor.toInt
}
/**
* Calculate the total number of nodes in a complete n-ary tree.
*
* @return The total number of nodes in the tree.
*/
val numberOfNodes: Int =
// The sum of nodes from level 0 to level d-1 is (n^(d+1) - 1) / (n - 1) if n > 1
// Else, the sum of nodes from level 0 to level d-1 is d
if (nsWidth == 1) {
nsDepth
} else {
((math.pow(nsWidth, nsDepth) - 1) / (nsWidth - 1)).toInt
}
/**
* Returns a range of ordinals for the nodes on the last level of a complete n-ary tree.
*
* @return The range of ordinals for the nodes on the last level of the tree.
*/
val lastLevelOrdinals: Range = {
val lastLevel = nsDepth - 1
if (nsWidth == 1) {
// For a unary tree, the only node at depth d is simply the node with ordinal d.
Range.inclusive(lastLevel, lastLevel)
} else {
// The sum of nodes from level 0 to level d-1 is (n^d - 1) / (n - 1)
val start = ((math.pow(nsWidth, lastLevel) - 1) / (nsWidth - 1)).toInt
// The sum of nodes from level 0 to level d is (n^(d+1) - 1) / (n - 1)
// Therefore, the last ordinal at depth d is:
val end = (((math.pow(nsWidth, lastLevel + 1) - 1) / (nsWidth - 1)).toInt) - 1
Range.inclusive(start, end)
}
}
val numberOfLastLevelElements: Int = {
val lastLevel = nsDepth - 1
math.pow(nsWidth, lastLevel).toInt
}
/**
* Computes the ordinals of all sibling nodes for a given node.
*
* @param ordinal the ordinal of the node
* @return a list of ordinals representing the sibling nodes (excluding the node itself)
*/
def siblingsOf(ordinal: Int): List[Int] =
if (ordinal == 0) {
// Root node has no siblings
List.empty
} else {
// Get parent ordinal
val parent = (ordinal - 1) / nsWidth
// Get all children of parent (siblings including self) and exclude self
childrenOf(parent).filter(_ != ordinal)
}
}