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

commonMain.IntervalUnionComparison.kt Maven / Gradle / Ivy

package io.github.whathecode.kotlinx.interval


/**
 * The result of comparing how two non-empty [IntervalUnion]'s are positioned in relation to one another.
 */
class IntervalUnionComparison<
    T : Comparable,
    TSize : Comparable,
    TUnion : IntervalUnion
> private constructor(
    /**
     * The union with the smallest starting value out of the two unions,
     * or if equal, the first union passed to the comparison.
     */
    val lower: TUnion,
    /**
     * The bounds of [lower].
     */
    val lowerBounds: Interval,
    /**
     * The union with the largest starting value out of the two unions,
     * or if equals, the second union passed to the comparison.
     */
    val upper: TUnion,
    /**
     * The bounds of [upper].
     */
    val upperBounds: Interval,
    /**
     * Determines whether [lower] precedes [upper] with at least some values of [T] lying in between.
     */
    val isSplitPair: Boolean,
    /**
     * Determines whether [lowerBounds] fully encompasses [upperBounds].
     * I.e., if [lower] and [upper] are [Interval]'s, all values of [upper] are also contained in [lower].
     */
    val isFullyEncompassed: Boolean
)
{
    companion object
    {
        /**
         * Compare how [union1] and [union2] are positioned in relation to one another.
         * Both need to be non-empty.
         *
         * @throws IllegalArgumentException if one of the two unions is empty.
         */
        fun , TSize : Comparable, TUnion : IntervalUnion> of(
            union1: TUnion,
            union2: TUnion
        ): IntervalUnionComparison
        {
            // Start with assuming `union1` starts before `union2`.
            var lowerBounds = requireNotNull( union1.getBounds() ) { "`union1` shouldn't be empty." }
            var upperBounds = requireNotNull( union2.getBounds() ) { "`union2` shouldn't be empty." }
            var lower: TUnion = union1
            var upper: TUnion = union2

            // Swap if `union2` starts before.
            val swapCompare = upperBounds.start.compareTo( lowerBounds.start )
            if ( swapCompare < 0 || (swapCompare == 0 && upperBounds.isStartIncluded && !lowerBounds.isStartIncluded) )
            {
                lowerBounds = upperBounds.also { upperBounds = lowerBounds }
                lower = upper.also { upper = lower }
            }

            // Determine whether any values lie between both intervals.
            val operations = lowerBounds.valueOperations
            val gap = upperBounds.start.compareTo( lowerBounds.end )
            val isSplit =
                // Shared endpoints which neither interval includes means there is a gap.
                if ( gap == 0 && !(lowerBounds.isEndIncluded || upperBounds.isStartIncluded) ) true
                // For non-evenly spaced types (real numbers), any gap is sufficient.
                else if ( operations.spacing == null ) gap > 0
                // For evenly spaced types, the gap needs to be larger than the space between subsequent values.
                else gap > 0 && operations.unsafeSubtract( upperBounds.start, lowerBounds.end ) > operations.spacing!!

            // Determine whether upper is fully encompassed by lower.
            val endCompare = upperBounds.end.compareTo( lowerBounds.end )
            val isFullyEncompassed = endCompare < 0 ||
                (endCompare == 0 && !upperBounds.isEndIncluded && lowerBounds.isEndIncluded)

            return IntervalUnionComparison( lower, lowerBounds, upper, upperBounds, isSplit, isFullyEncompassed )
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy