HexCasting/Common/src/main/java/at/petrak/hexcasting/common/casting/arithmetic/DoubleArithmetic.kt
2023-06-04 16:16:11 +10:00

82 lines
3.4 KiB
Kotlin

package at.petrak.hexcasting.common.casting.arithmetic
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.*
import at.petrak.hexcasting.api.casting.arithmetic.engine.InvalidOperatorException
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBinary
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorUnary
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaMultiPredicate
import at.petrak.hexcasting.api.casting.arithmetic.predicates.IotaPredicate
import at.petrak.hexcasting.api.casting.iota.DoubleIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.MishapDivideByZero
import at.petrak.hexcasting.common.casting.arithmetic.operator.OperatorLog
import at.petrak.hexcasting.common.casting.arithmetic.operator.asDoubleBetween
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import java.util.function.DoubleBinaryOperator
import java.util.function.DoubleUnaryOperator
import kotlin.math.*
object DoubleArithmetic : Arithmetic {
@JvmField
val OPS = listOf(
ADD,
SUB,
MUL,
DIV,
ABS,
POW,
FLOOR,
CEIL,
SIN,
COS,
TAN,
ARCSIN,
ARCCOS,
ARCTAN,
ARCTAN2,
LOG,
MOD
)
/**
* An example of an IotaMultiPredicate, which returns true only if all arguments to the Operator are DoubleIotas.
*/
val ACCEPTS: IotaMultiPredicate = IotaMultiPredicate.all(IotaPredicate.ofType(HexIotaTypes.DOUBLE))
override fun arithName() = "double_math"
override fun opTypes() = OPS
override fun getOperator(pattern: HexPattern): Operator {
return when (pattern) {
ADD -> make2 { a, b -> a + b }
SUB -> make2 { a, b -> a - b }
MUL -> make2 { a, b -> a * b }
DIV -> make2 { a, b -> if (b == 0.0) throw MishapDivideByZero.of(a, b) else a / b }
ABS -> make1 { a -> abs(a) }
POW -> make2 { a, b -> a.pow(b) }
FLOOR -> make1 { a -> floor(a) }
CEIL -> make1 { a -> ceil(a) }
SIN -> make1 { a -> sin(a) }
COS -> make1 { a -> cos(a) }
TAN -> make1 { a -> if (cos(a) == 0.0) throw MishapDivideByZero.tan(a) else tan(a) }
ARCSIN -> make1 { a -> asin(a.asDoubleBetween(-1.0, 1.0, 0)) }
ARCCOS -> make1 { a -> acos(a.asDoubleBetween(-1.0, 1.0, 0)) }
ARCTAN -> make1 { a -> atan(a) }
ARCTAN2 -> make2 { a, b -> atan2(a, b) }
LOG -> OperatorLog
MOD -> make2 { a, b -> if (b == 0.0) throw MishapDivideByZero.of(a, b) else a % b }
else -> throw InvalidOperatorException("$pattern is not a valid operator in Arithmetic $this.")
}
}
fun make1(op: DoubleUnaryOperator) = OperatorUnary(ACCEPTS)
{ i: Iota -> DoubleIota(op.applyAsDouble(Operator.downcast(i, HexIotaTypes.DOUBLE).double)) }
fun make2(op: DoubleBinaryOperator) = OperatorBinary(ACCEPTS)
{ i: Iota, j: Iota -> DoubleIota(op.applyAsDouble(Operator.downcast(i, HexIotaTypes.DOUBLE).double, Operator.downcast(j, HexIotaTypes.DOUBLE).double)) }
}