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

fuookami.ospf.kotlin.utils.math.value_range.ValueRange.kt Maven / Gradle / Ivy

There is a newer version: 1.0.31
Show newest version
package fuookami.ospf.kotlin.utils.math.value_range

import java.util.*
import kotlin.reflect.full.*
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import fuookami.ospf.kotlin.utils.concept.*
import fuookami.ospf.kotlin.utils.math.*
import fuookami.ospf.kotlin.utils.math.ordinary.*
import fuookami.ospf.kotlin.utils.error.*
import fuookami.ospf.kotlin.utils.operator.*
import fuookami.ospf.kotlin.utils.functional.*

open class ValueRangeSerializer(
    private val valueSerializer: ValueWrapperSerializer
) : KSerializer> where T : RealNumber, T : NumberField {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ValueRange") {
        element("lowerBound")
        element("upperBound")
        element("lowerInterval")
        element("upperInterval")
    }

    override fun serialize(encoder: Encoder, value: ValueRange) {
        require(encoder is JsonEncoder)
        encoder.encodeJsonElement(
            buildJsonObject {
                put("lowerBound", encoder.json.encodeToJsonElement(valueSerializer, value.lowerBound.value))
                put("upperBound", encoder.json.encodeToJsonElement(valueSerializer, value.upperBound.value))
                put("lowerInterval", value.lowerBound.interval.toString().lowercase(Locale.getDefault()))
                put("upperInterval", value.upperBound.interval.toString().lowercase(Locale.getDefault()))
            }
        )
    }

    @OptIn(ExperimentalSerializationApi::class)
    override fun deserialize(decoder: Decoder): ValueRange {
        require(decoder is JsonDecoder)
        val element = decoder.decodeJsonElement()
        require(element is JsonObject)
        require(descriptor.elementNames.all { it in element })
        return ValueRange(
            lowerBound = Bound(
                decoder.json.decodeFromJsonElement(valueSerializer, element["lowerBound"]!!),
                Interval.valueOf(element["lowerInterval"]!!.jsonPrimitive.content.replaceFirstChar {
                    if (it.isLowerCase()) it.titlecase(
                        Locale.getDefault()
                    ) else it.toString()
                })
            ),
            upperBound = Bound(
                decoder.json.decodeFromJsonElement(valueSerializer, element["upperBound"]!!),
                Interval.valueOf(element["upperInterval"]!!.jsonPrimitive.content.replaceFirstChar {
                    if (it.isLowerCase()) it.titlecase(
                        Locale.getDefault()
                    ) else it.toString()
                }),
            ),
            constants = valueSerializer.constants
        )
    }
}

data object ValueRangeInt64Serializer : ValueRangeSerializer(ValueWrapperSerializer())
data object ValueRangeUInt64Serializer : ValueRangeSerializer(ValueWrapperSerializer())
data object ValueRangeFlt64Serializer : ValueRangeSerializer(ValueWrapperSerializer())

data class ValueRange(
    val lowerBound: Bound,
    val upperBound: Bound,
    private val constants: RealNumberConstants
) : Cloneable, Copyable>, Eq>,
    Plus, ValueRange>, Minus, ValueRange>,
    Times, ValueRange?>, Div?>
        where T : RealNumber, T : NumberField {
    companion object {
        fun  empty(
            lb: ValueWrapper,
            ub: ValueWrapper,
            lbInterval: Interval,
            ubInterval: Interval
        ): Boolean where T : RealNumber, T : NumberField {
            return if (lb.isNegativeInfinity) {
                false
            } else if (ub.isInfinity) {
                false
            } else if (!lb.isInfinityOrNegativeInfinity && !ub.isInfinityOrNegativeInfinity) {
                !lbInterval.lowerBoundOperator()(lb.unwrap(), ub.unwrap()) || !ubInterval.upperBoundOperator()(ub.unwrap(), lb.unwrap())
            } else {
                true
            }
        }

        @Suppress("UNCHECKED_CAST")
        inline operator fun  invoke(): ValueRange where T : RealNumber, T : NumberField {
            val constants = (T::class.companionObjectInstance!! as RealNumberConstants)
            return ValueRange(
                Bound(ValueWrapper.NegativeInfinity(constants), Interval.Closed),
                Bound(ValueWrapper.Infinity(constants), Interval.Closed),
                constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline operator fun  invoke(
            value: T
        ): Ret> where T : RealNumber, T : NumberField {
            return invoke(value, (T::class.companionObjectInstance!! as RealNumberConstants))
        }

        operator fun  invoke(
            value: T,
            constants: RealNumberConstants
        ): Ret> where T : RealNumber, T : NumberField {
            return ValueRange(
                value,
                value,
                Interval.Closed,
                Interval.Closed,
                constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline operator fun  invoke(
            lb: T,
            ub: T,
            lbInterval: Interval = Interval.Closed,
            ubInterval: Interval = Interval.Closed
        ): Ret> where T : RealNumber, T : NumberField {
            return ValueRange(
                lb = lb,
                ub = ub,
                lbInterval = lbInterval,
                ubInterval = ubInterval,
                constants = (T::class.companionObjectInstance!! as RealNumberConstants)
            )
        }

        operator fun  invoke(
            lb: T,
            ub: T,
            lbInterval: Interval,
            ubInterval: Interval,
            constants: RealNumberConstants
        ): Ret> where T : RealNumber, T : NumberField {
            val lowerBound = when (val result = ValueWrapper(lb, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    return Failed(result.error)
                }
            }
            val upperBound = when (val result = ValueWrapper(ub, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    return Failed(result.error)
                }
            }
            return ValueRange(
                lb = lowerBound,
                ub = upperBound,
                lbInterval = lbInterval,
                ubInterval = ubInterval,
                constants = constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline operator fun  invoke(
            lb: T,
            ub: Infinity,
            lbInterval: Interval = Interval.Closed
        ): Ret> where T : RealNumber, T : NumberField {
            val constants = (T::class.companionObjectInstance!! as RealNumberConstants)
            val lowerBound = when (val result = ValueWrapper(lb, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    return Failed(result.error)
                }
            }
            return ValueRange(
                lb = lowerBound,
                ub = ValueWrapper.Infinity(constants),
                lbInterval = lbInterval,
                ubInterval = Interval.Open,
                constants = constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline operator fun  invoke(
            lb: NegativeInfinity,
            ub: T,
            ubInterval: Interval = Interval.Closed
        ): Ret> where T : RealNumber, T : NumberField {
            val constants = (T::class.companionObjectInstance!! as RealNumberConstants)
            val upperBound = when (val result = ValueWrapper(ub, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    return Failed(result.error)
                }
            }
            return ValueRange(
                lb = ValueWrapper.NegativeInfinity(constants),
                ub = upperBound,
                lbInterval = Interval.Open,
                ubInterval = ubInterval,
                constants = constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline fun  geq(
            lb: T,
            lbInterval: Interval = Interval.Closed
        ): Ret> where T : RealNumber, T : NumberField {
            return geq(lb, lbInterval, (T::class.companionObjectInstance!! as RealNumberConstants))
        }

        @Suppress("UNCHECKED_CAST")
        inline fun  gr(
            lb: T
        ): Ret> where T : RealNumber, T : NumberField {
            return geq(lb, Interval.Open, (T::class.companionObjectInstance!! as RealNumberConstants))
        }

        fun  geq(
            lb: T,
            lbInterval: Interval,
            constants: RealNumberConstants
        ): Ret> where T : RealNumber, T : NumberField {
            val lowerBound = when (val result = ValueWrapper(lb, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    return Failed(result.error)
                }
            }
            return invoke(
                lb = lowerBound,
                ub = ValueWrapper.Infinity(constants),
                lbInterval = lbInterval,
                ubInterval = Interval.Open,
                constants = constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline fun  leq(
            ub: T,
            lbInterval: Interval = Interval.Closed
        ): Ret> where T : RealNumber, T : NumberField {
            return leq(ub, lbInterval, (T::class.companionObjectInstance!! as RealNumberConstants))
        }

        @Suppress("UNCHECKED_CAST")
        inline fun  ls(
            ub: T
        ): Ret> where T : RealNumber, T : NumberField {
            return leq(ub, Interval.Open, (T::class.companionObjectInstance!! as RealNumberConstants))
        }

        fun  leq(
            ub: T,
            lbInterval: Interval,
            constants: RealNumberConstants
        ): Ret> where T : RealNumber, T : NumberField {
            val upperBound = when (val result = ValueWrapper(ub, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    return Failed(result.error)
                }
            }
            return invoke(
                lb = ValueWrapper.NegativeInfinity(constants),
                ub = upperBound,
                lbInterval = Interval.Open,
                ubInterval = lbInterval,
                constants = constants
            )
        }

        @Suppress("UNCHECKED_CAST")
        inline operator fun  invoke(
            lb: ValueWrapper,
            ub: ValueWrapper,
            lbInterval: Interval,
            ubInterval: Interval
        ): Ret> where T : RealNumber, T : NumberField {
            return ValueRange(
                lb = lb,
                ub = ub,
                lbInterval = lbInterval,
                ubInterval = ubInterval,
                constants = (T::class.companionObjectInstance!! as RealNumberConstants)
            )
        }

        operator fun  invoke(
            lb: ValueWrapper,
            ub: ValueWrapper,
            lbInterval: Interval,
            ubInterval: Interval,
            constants: RealNumberConstants
        ): Ret> where T : RealNumber, T : NumberField {
            return if (!empty(lb, ub, lbInterval, ubInterval)) {
                Ok(
                    ValueRange(
                        lowerBound = Bound(lb, lbInterval),
                        upperBound = Bound(ub, ubInterval),
                        constants = constants
                    )
                )
            } else {
                Failed(
                    Err(
                        code = ErrorCode.IllegalArgument,
                        message = "Invalid range: ${lbInterval.lowerSign}$lb, $ub${ubInterval.upperSign}"
                    )
                )
            }
        }
    }

    val mean by lazy {
        (lowerBound.value + upperBound.value) / constants.two
    }

    val diff by lazy {
        upperBound.value - lowerBound.value
    }

    val gap by lazy {
        try {
            diff / max(constants.decimalPrecision, abs(mean.unwrap()))
        } catch (e: Exception) {
            e.printStackTrace()
            constants.nan!!
        }
    }

    val fixed: Boolean by lazy {
        if (lowerBound.interval != Interval.Closed || upperBound.interval != Interval.Closed) {
            false
        } else {
            if (!lowerBound.value.isInfinityOrNegativeInfinity && !upperBound.value.isInfinityOrNegativeInfinity) {
                lowerBound.value.unwrap() eq upperBound.value.unwrap()
            } else {
                false
            }
        }
    }

    val fixedValue: T? by lazy {
        if (fixed) {
            lowerBound.value.unwrap()
        } else {
            null
        }
    }

    infix fun union(rhs: ValueRange): ValueRange? {
        if (upperBound.value ls rhs.lowerBound.value || rhs.upperBound.value ls lowerBound.value) {
            return null
        }

        val newLb = when (lowerBound.value ord rhs.lowerBound.value) {
            is Order.Less -> lowerBound.value
            else -> rhs.lowerBound.value
        }
        val newLbInterval = when (lowerBound.value ord rhs.lowerBound.value) {
            is Order.Less -> lowerBound.interval
            is Order.Greater -> rhs.lowerBound.interval
            else -> lowerBound.interval union rhs.lowerBound.interval
        }
        val newUb = when (upperBound.value ord rhs.upperBound.value) {
            is Order.Less -> rhs.upperBound.value
            else -> upperBound.value
        }
        val newUbInterval = when (upperBound.value ord rhs.upperBound.value) {
            is Order.Less -> rhs.upperBound.interval
            is Order.Greater -> upperBound.interval
            else -> upperBound.interval union rhs.upperBound.interval
        }
        return when (val result = ValueRange(newLb, newUb, newLbInterval, newUbInterval, constants)) {
            is Ok -> {
                result.value
            }

            is Failed -> {
                null
            }
        }
    }

    infix fun intersect(rhs: ValueRange): ValueRange? {
        val newLb = when (lowerBound.value ord rhs.lowerBound.value) {
            is Order.Less -> rhs.lowerBound.value
            else -> lowerBound.value
        }
        val newLbInterval = if (lowerBound.value.isInfinityOrNegativeInfinity) {
            rhs.lowerBound.interval
        } else if (rhs.lowerBound.value.isInfinityOrNegativeInfinity) {
            lowerBound.interval
        } else {
            when (lowerBound.value ord rhs.lowerBound.value) {
                is Order.Less -> rhs.lowerBound.interval
                is Order.Greater -> lowerBound.interval
                else -> lowerBound.interval intersect rhs.lowerBound.interval
            }
        }
        val newUb = when (upperBound.value ord rhs.upperBound.value) {
            is Order.Less -> upperBound.value
            else -> rhs.upperBound.value
        }
        val newUbInterval = if (upperBound.value.isInfinityOrNegativeInfinity) {
            rhs.upperBound.interval
        } else if (rhs.upperBound.value.isInfinityOrNegativeInfinity) {
            upperBound.interval
        } else {
            when (upperBound.value ord rhs.upperBound.value) {
                is Order.Less -> upperBound.interval
                is Order.Greater -> rhs.upperBound.interval
                else -> upperBound.interval intersect rhs.upperBound.interval
            }
        }
        return when (val result = ValueRange(newLb, newUb, newLbInterval, newUbInterval, constants)) {
            is Ok -> {
                result.value
            }

            is Failed -> {
                null
            }
        }
    }

    infix fun contains(value: T): Boolean {
        return if (lowerBound.value.isNegativeInfinity || upperBound.value.isInfinity) {
            true
        } else if (!lowerBound.value.isInfinityOrNegativeInfinity && !upperBound.value.isInfinityOrNegativeInfinity) {
            val lhs = lowerBound.interval.lowerBoundOperator()(lowerBound.value.unwrap(), value)
            val rhs = upperBound.interval.upperBoundOperator()(upperBound.value.unwrap(), value)
            lhs && rhs
        } else {
            false
        }
    }

    infix fun contains(valueRange: ValueRange): Boolean {
        return if (lowerBound.value.isNegativeInfinity || upperBound.value.isInfinity) {
            true
        } else if (!lowerBound.value.isInfinityOrNegativeInfinity && !upperBound.value.isInfinityOrNegativeInfinity
            && !valueRange.lowerBound.value.isInfinityOrNegativeInfinity && !valueRange.upperBound.value.isInfinityOrNegativeInfinity
        ) {
            val lbInterval = lowerBound.interval intersect valueRange.lowerBound.interval
            val ubInterval = upperBound.interval intersect valueRange.upperBound.interval
            val lhs = lbInterval.lowerBoundOperator()(lowerBound.value.unwrap(), valueRange.lowerBound.value.unwrap())
            val rhs = ubInterval.upperBoundOperator()(upperBound.value.unwrap(), valueRange.upperBound.value.unwrap())
            lhs && rhs
        } else {
            false
        }
    }

    override fun copy(): ValueRange {
        return ValueRange(
            lowerBound = lowerBound.copy(),
            upperBound = upperBound.copy(),
            constants = constants
        )
    }

    override fun partialEq(rhs: ValueRange): Boolean? {
        when (val result = lowerBound partialEq rhs.lowerBound) {
            false, null -> {
                return result
            }

            else -> {}
        }

        when (val result = upperBound partialEq rhs.upperBound) {
            false, null -> {
                return result
            }

            else -> {}
        }

        return true
    }

    operator fun plus(rhs: T): ValueRange {
        return ValueRange(
            lowerBound = lowerBound + rhs,
            upperBound = upperBound + rhs,
            constants = constants
        )
    }

    override fun plus(rhs: ValueRange): ValueRange {
        return ValueRange(
            lowerBound = lowerBound + rhs.lowerBound,
            upperBound = upperBound + rhs.upperBound,
            constants = constants
        )
    }

    operator fun minus(rhs: T): ValueRange {
        return ValueRange(
            lowerBound = lowerBound - rhs,
            upperBound = upperBound - rhs,
            constants = constants
        )
    }

    override fun minus(rhs: ValueRange): ValueRange {
        return ValueRange(
            lowerBound = lowerBound - rhs.upperBound,
            upperBound = upperBound - rhs.lowerBound,
            constants = constants
        )
    }

    operator fun times(rhs: T): ValueRange? {
        return if (rhs gr constants.zero) {
            try {
                ValueRange(lowerBound * rhs, upperBound * rhs, constants)
            } catch (e: Exception) {
                e.printStackTrace()
                null
            }
        } else if (rhs ls constants.zero) {
            try {
                ValueRange(upperBound * rhs, lowerBound * rhs, constants)
            } catch (e: Exception) {
                e.printStackTrace()
                null
            }
        } else {
            when (val result = ValueRange(constants.zero, constants.zero, lowerBound.interval, upperBound.interval, constants)) {
                is Ok -> {
                    result.value
                }

                is Failed -> {
                    null
                }
            }
        }
    }

    override fun times(rhs: ValueRange): ValueRange? {
        val bounds = try {
            listOf(
                Bound(lowerBound.value * rhs.lowerBound.value, lowerBound.interval intersect rhs.lowerBound.interval),
                Bound(lowerBound.value * rhs.upperBound.value, lowerBound.interval intersect rhs.upperBound.interval),
                Bound(upperBound.value * rhs.lowerBound.value, upperBound.interval intersect rhs.lowerBound.interval),
                Bound(upperBound.value * rhs.upperBound.value, upperBound.interval intersect rhs.upperBound.interval)
            ).sortedWithThreeWayComparator { l, r -> l ord r }
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        }
        return ValueRange(
            bounds.first(),
            bounds.last(),
            constants
        )
    }

    override fun div(rhs: T): ValueRange? {
        return if (rhs eq constants.zero) {
            null
        } else {
            times(rhs.reciprocal())
        }
    }

    fun toFlt64() = ValueRange(
        lowerBound.toFlt64(),
        upperBound.toFlt64(),
        Flt64
    )

    @Suppress("UNCHECKED_CAST")
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as ValueRange<*>
        if (constants != other.constants) return false

        other as ValueRange
        if (lowerBound neq other.lowerBound) return false
        if (upperBound neq other.upperBound) return false

        return true
    }

    override fun hashCode(): Int {
        var result = lowerBound.hashCode()
        result = 31 * result + upperBound.hashCode()
        result = 31 * result + constants.hashCode()
        return result
    }

    override fun toString(): String = "${lowerBound.interval.lowerSign}${lowerBound.value}, ${upperBound.value}${upperBound.interval.upperSign}"
}

operator fun  T.plus(valueRange: ValueRange): ValueRange where T : RealNumber, T : NumberField {
    return valueRange + this
}

operator fun  T.times(valueRange: ValueRange): ValueRange? where T : RealNumber, T : NumberField {
    return valueRange * this
}

@JvmName("negValueRangeFlt32")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    Flt32
)

@JvmName("negValueRangeFlt64")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    Flt64
)

@JvmName("negValueRangeFltX")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    FltX
)

@JvmName("negValueRangeInt8")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    Int8
)

@JvmName("negValueRangeInt16")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    Int16
)

@JvmName("negValueRangeInt32")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    Int32
)

@JvmName("negValueRangeInt64")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    Int64
)

@JvmName("negValueRangeIntX")
operator fun ValueRange.unaryMinus() = ValueRange(
    -upperBound,
    -lowerBound,
    IntX
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy