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

main.io.ksmt.solver.cvc5.KCvc5ExprInternalizer.kt Maven / Gradle / Ivy

The newest version!
package io.ksmt.solver.cvc5

import io.github.cvc5.Kind
import io.github.cvc5.RoundingMode
import io.github.cvc5.Solver
import io.github.cvc5.Sort
import io.github.cvc5.Term
import io.ksmt.decl.KDecl
import io.ksmt.expr.KAddArithExpr
import io.ksmt.expr.KAndBinaryExpr
import io.ksmt.expr.KAndExpr
import io.ksmt.expr.KArray2Lambda
import io.ksmt.expr.KArray2Select
import io.ksmt.expr.KArray2Store
import io.ksmt.expr.KArray3Lambda
import io.ksmt.expr.KArray3Select
import io.ksmt.expr.KArray3Store
import io.ksmt.expr.KArrayConst
import io.ksmt.expr.KArrayLambda
import io.ksmt.expr.KArrayLambdaBase
import io.ksmt.expr.KArrayNLambda
import io.ksmt.expr.KArrayNSelect
import io.ksmt.expr.KArrayNStore
import io.ksmt.expr.KArraySelect
import io.ksmt.expr.KArraySelectBase
import io.ksmt.expr.KArrayStore
import io.ksmt.expr.KArrayStoreBase
import io.ksmt.expr.KBitVec16Value
import io.ksmt.expr.KBitVec1Value
import io.ksmt.expr.KBitVec32Value
import io.ksmt.expr.KBitVec64Value
import io.ksmt.expr.KBitVec8Value
import io.ksmt.expr.KBitVecCustomValue
import io.ksmt.expr.KBitVecNumberValue
import io.ksmt.expr.KBitVecValue
import io.ksmt.expr.KBv2IntExpr
import io.ksmt.expr.KBvAddExpr
import io.ksmt.expr.KBvAddNoOverflowExpr
import io.ksmt.expr.KBvAddNoUnderflowExpr
import io.ksmt.expr.KBvAndExpr
import io.ksmt.expr.KBvArithShiftRightExpr
import io.ksmt.expr.KBvConcatExpr
import io.ksmt.expr.KBvDivNoOverflowExpr
import io.ksmt.expr.KBvExtractExpr
import io.ksmt.expr.KBvLogicalShiftRightExpr
import io.ksmt.expr.KBvMulExpr
import io.ksmt.expr.KBvMulNoOverflowExpr
import io.ksmt.expr.KBvMulNoUnderflowExpr
import io.ksmt.expr.KBvNAndExpr
import io.ksmt.expr.KBvNegNoOverflowExpr
import io.ksmt.expr.KBvNegationExpr
import io.ksmt.expr.KBvNorExpr
import io.ksmt.expr.KBvNotExpr
import io.ksmt.expr.KBvOrExpr
import io.ksmt.expr.KBvReductionAndExpr
import io.ksmt.expr.KBvReductionOrExpr
import io.ksmt.expr.KBvRepeatExpr
import io.ksmt.expr.KBvRotateLeftExpr
import io.ksmt.expr.KBvRotateLeftIndexedExpr
import io.ksmt.expr.KBvRotateRightExpr
import io.ksmt.expr.KBvRotateRightIndexedExpr
import io.ksmt.expr.KBvShiftLeftExpr
import io.ksmt.expr.KBvSignExtensionExpr
import io.ksmt.expr.KBvSignedDivExpr
import io.ksmt.expr.KBvSignedGreaterExpr
import io.ksmt.expr.KBvSignedGreaterOrEqualExpr
import io.ksmt.expr.KBvSignedLessExpr
import io.ksmt.expr.KBvSignedLessOrEqualExpr
import io.ksmt.expr.KBvSignedModExpr
import io.ksmt.expr.KBvSignedRemExpr
import io.ksmt.expr.KBvSubExpr
import io.ksmt.expr.KBvSubNoOverflowExpr
import io.ksmt.expr.KBvSubNoUnderflowExpr
import io.ksmt.expr.KBvToFpExpr
import io.ksmt.expr.KBvUnsignedDivExpr
import io.ksmt.expr.KBvUnsignedGreaterExpr
import io.ksmt.expr.KBvUnsignedGreaterOrEqualExpr
import io.ksmt.expr.KBvUnsignedLessExpr
import io.ksmt.expr.KBvUnsignedLessOrEqualExpr
import io.ksmt.expr.KBvUnsignedRemExpr
import io.ksmt.expr.KBvXNorExpr
import io.ksmt.expr.KBvXorExpr
import io.ksmt.expr.KBvZeroExtensionExpr
import io.ksmt.expr.KConst
import io.ksmt.expr.KDistinctExpr
import io.ksmt.expr.KDivArithExpr
import io.ksmt.expr.KEqExpr
import io.ksmt.expr.KExistentialQuantifier
import io.ksmt.expr.KExpr
import io.ksmt.expr.KFalse
import io.ksmt.expr.KFp128Value
import io.ksmt.expr.KFp16Value
import io.ksmt.expr.KFp32Value
import io.ksmt.expr.KFp64Value
import io.ksmt.expr.KFpAbsExpr
import io.ksmt.expr.KFpAddExpr
import io.ksmt.expr.KFpCustomSizeValue
import io.ksmt.expr.KFpDivExpr
import io.ksmt.expr.KFpEqualExpr
import io.ksmt.expr.KFpFromBvExpr
import io.ksmt.expr.KFpFusedMulAddExpr
import io.ksmt.expr.KFpGreaterExpr
import io.ksmt.expr.KFpGreaterOrEqualExpr
import io.ksmt.expr.KFpIsInfiniteExpr
import io.ksmt.expr.KFpIsNaNExpr
import io.ksmt.expr.KFpIsNegativeExpr
import io.ksmt.expr.KFpIsNormalExpr
import io.ksmt.expr.KFpIsPositiveExpr
import io.ksmt.expr.KFpIsSubnormalExpr
import io.ksmt.expr.KFpIsZeroExpr
import io.ksmt.expr.KFpLessExpr
import io.ksmt.expr.KFpLessOrEqualExpr
import io.ksmt.expr.KFpMaxExpr
import io.ksmt.expr.KFpMinExpr
import io.ksmt.expr.KFpMulExpr
import io.ksmt.expr.KFpNegationExpr
import io.ksmt.expr.KFpRemExpr
import io.ksmt.expr.KFpRoundToIntegralExpr
import io.ksmt.expr.KFpRoundingMode
import io.ksmt.expr.KFpRoundingModeExpr
import io.ksmt.expr.KFpSqrtExpr
import io.ksmt.expr.KFpSubExpr
import io.ksmt.expr.KFpToBvExpr
import io.ksmt.expr.KFpToFpExpr
import io.ksmt.expr.KFpToIEEEBvExpr
import io.ksmt.expr.KFpToRealExpr
import io.ksmt.expr.KFpValue
import io.ksmt.expr.KFunctionApp
import io.ksmt.expr.KFunctionAsArray
import io.ksmt.expr.KGeArithExpr
import io.ksmt.expr.KGtArithExpr
import io.ksmt.expr.KImpliesExpr
import io.ksmt.expr.KInt32NumExpr
import io.ksmt.expr.KInt64NumExpr
import io.ksmt.expr.KIntBigNumExpr
import io.ksmt.expr.KInterpretedValue
import io.ksmt.expr.KIsIntRealExpr
import io.ksmt.expr.KIteExpr
import io.ksmt.expr.KLeArithExpr
import io.ksmt.expr.KLtArithExpr
import io.ksmt.expr.KModIntExpr
import io.ksmt.expr.KMulArithExpr
import io.ksmt.expr.KNotExpr
import io.ksmt.expr.KOrBinaryExpr
import io.ksmt.expr.KOrExpr
import io.ksmt.expr.KPowerArithExpr
import io.ksmt.expr.KRealNumExpr
import io.ksmt.expr.KRealToFpExpr
import io.ksmt.expr.KRemIntExpr
import io.ksmt.expr.KSubArithExpr
import io.ksmt.expr.KToIntRealExpr
import io.ksmt.expr.KToRealIntExpr
import io.ksmt.expr.KTrue
import io.ksmt.expr.KUnaryMinusArithExpr
import io.ksmt.expr.KUninterpretedSortValue
import io.ksmt.expr.KUniversalQuantifier
import io.ksmt.expr.KXorExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvAddNoOverflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvAddNoUnderflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvDivNoOverflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvNegNoOverflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvSubNoOverflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvSubNoUnderflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvMulNoOverflowExpr
import io.ksmt.expr.rewrite.simplify.rewriteBvMulNoUnderflowExpr
import io.ksmt.expr.rewrite.simplify.simplifyBvRotateLeftExpr
import io.ksmt.expr.rewrite.simplify.simplifyBvRotateRightExpr
import io.ksmt.solver.KSolverUnsupportedFeatureException
import io.ksmt.solver.util.KExprInternalizerBase
import io.ksmt.sort.KArithSort
import io.ksmt.sort.KArray2Sort
import io.ksmt.sort.KArray3Sort
import io.ksmt.sort.KArrayNSort
import io.ksmt.sort.KArraySortBase
import io.ksmt.sort.KBoolSort
import io.ksmt.sort.KBvSort
import io.ksmt.sort.KFp128Sort
import io.ksmt.sort.KFp16Sort
import io.ksmt.sort.KFp32Sort
import io.ksmt.sort.KFp64Sort
import io.ksmt.sort.KFpRoundingModeSort
import io.ksmt.sort.KFpSort
import io.ksmt.sort.KRealSort
import io.ksmt.sort.KSort
import io.ksmt.sort.KUninterpretedSort
import io.ksmt.utils.powerOfTwo
import java.math.BigInteger

@Suppress("LargeClass")
class KCvc5ExprInternalizer(
    private val cvc5Ctx: KCvc5Context,
    private val solver: Solver
) : KExprInternalizerBase() {
    private val sortInternalizer = KCvc5SortInternalizer(cvc5Ctx)
    private val declInternalizer = KCvc5DeclInternalizer(cvc5Ctx, solver, sortInternalizer)

    private val tm: KCvc5TermManager = cvc5Ctx.termManager

    override fun findInternalizedExpr(expr: KExpr<*>): Term? = cvc5Ctx.findInternalizedExpr(expr)

    override fun saveInternalizedExpr(expr: KExpr<*>, internalized: Term) {
        cvc5Ctx.saveInternalizedExpr(expr, internalized)
    }

    private val zeroIntValueTerm: Term by lazy {
        val ksmtZero = cvc5Ctx.ctx.mkIntNum(0)
        findInternalizedExpr(ksmtZero)
            ?: tm.builder { mkInteger(0L) }.also { saveInternalizedExpr(ksmtZero, it) }
    }

    fun > T.internalizeDecl(): Term = accept(declInternalizer)

    fun  T.internalizeSort(): Sort = accept(sortInternalizer)

    override fun  transform(expr: KFunctionApp) = with(expr) {
        transformArray(args) { args: Array ->
            cvc5Ctx.addDeclaration(decl)

            // args[0] is a function declaration
            val decl = decl.internalizeDecl()

            if (decl.hasOp()) {
                val op = tm.termOp(decl) { op }
                tm.mkTerm(op, args)
            } else {
                decl.mkFunctionApp(args.asList())
            }
        }
    }

    override fun  transform(expr: KConst) = with(expr) {
        transform {
            cvc5Ctx.addDeclaration(decl)

            decl.internalizeDecl()
        }
    }

    override fun transform(expr: KAndExpr) = with(expr) {
        transformArray(args) { args: Array -> mkAndTerm(args.asList()) }
    }

    override fun transform(expr: KAndBinaryExpr) = with(expr) {
        transform(lhs, rhs) { l: Term, r: Term -> tm.mkTerm(Kind.AND, l, r) }
    }

    override fun transform(expr: KOrExpr) = with(expr) {
        transformArray(args) { args: Array -> mkOrTerm(args.asList()) }
    }

    override fun transform(expr: KOrBinaryExpr) = with(expr) {
        transform(lhs, rhs) { l: Term, r: Term -> tm.mkTerm(Kind.OR, l, r) }
    }

    override fun transform(expr: KNotExpr) =
        with(expr) { transform(arg) { arg: Term -> tm.mkTerm(Kind.NOT, arg) } }

    override fun transform(expr: KImpliesExpr) = with(expr) {
        transform(p, q) { p: Term, q: Term ->
            tm.mkTerm(Kind.IMPLIES, p, q)
        }
    }

    override fun transform(expr: KXorExpr) = with(expr) {
        transform(a, b) { a: Term, b: Term ->
            tm.mkTerm(Kind.XOR, a, b)
        }
    }

    override fun transform(expr: KTrue) = expr.transform { tm.builder { mkTrue() } }

    override fun transform(expr: KFalse) = expr.transform { tm.builder { mkFalse() } }

    override fun  transform(expr: KEqExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            mkArraySpecificTerm(
                sort = expr.lhs.sort,
                arrayTerm = { mkArrayEqTerm(it, lhs, rhs) },
                default = { lhs.eqTerm(rhs) }
            )
        }
    }

    override fun  transform(expr: KDistinctExpr) = with(expr) {
        transformArray(args) { args: Array ->
            mkArraySpecificTerm(
                sort = expr.args.first().sort,
                arrayTerm = { mkArrayDistinctTerm(it, args.asList()) },
                default = { tm.mkTerm(Kind.DISTINCT, args) }
            )
        }
    }

    override fun  transform(expr: KIteExpr) = with(expr) {
        transform(
            condition, trueBranch, falseBranch
        ) { condition: Term, trueBranch: Term, falseBranch: Term ->
            mkArraySpecificTerm(
                sort = expr.trueBranch.sort,
                arrayTerm = { mkArrayIteTerm(it, condition, trueBranch, falseBranch) },
                default = { condition.iteTerm(trueBranch, falseBranch) }
            )
        }
    }

    fun  transformBitVecValue(expr: KBitVecValue) = expr.transform {
        val size = expr.decl.sort.sizeBits.toInt()
        when {
            expr is KBitVec1Value -> tm.builder { mkBitVector(size, if (expr.value) 1L else 0L) }
            // cvc5 can't create bitvector from negatives
            expr is KBitVecNumberValue<*, *> && expr.numberValue.toLong() >= 0 -> {
                tm.builder { mkBitVector(size, expr.numberValue.toLong()) }
            }

            else -> tm.builder { mkBitVector(size, expr.stringValue, 2) }
        }
    }

    override fun transform(expr: KBitVec1Value) = transformBitVecValue(expr)

    override fun transform(expr: KBitVec8Value) = transformBitVecValue(expr)

    override fun transform(expr: KBitVec16Value) = transformBitVecValue(expr)

    override fun transform(expr: KBitVec32Value) = transformBitVecValue(expr)

    override fun transform(expr: KBitVec64Value) = transformBitVecValue(expr)

    override fun transform(expr: KBitVecCustomValue) = transformBitVecValue(expr)

    override fun  transform(expr: KBvNotExpr) = with(expr) {
        transform(value) { value: Term -> tm.mkTerm(Kind.BITVECTOR_NOT, value) }
    }

    override fun  transform(expr: KBvReductionAndExpr) = with(expr) {
        transform(value) { value: Term -> tm.mkTerm(Kind.BITVECTOR_REDAND, value) }
    }

    override fun  transform(expr: KBvReductionOrExpr) = with(expr) {
        transform(value) { value: Term -> tm.mkTerm(Kind.BITVECTOR_REDOR, value) }
    }

    override fun  transform(expr: KBvAndExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_AND, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvOrExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_OR, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvXorExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_XOR, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvNAndExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_NAND, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvNorExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_NOR, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvXNorExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_XNOR, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvNegationExpr) = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.BITVECTOR_NEG, value)
        }
    }

    override fun  transform(expr: KBvAddExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_ADD, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSubExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SUB, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvMulExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_MULT, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvUnsignedDivExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_UDIV, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedDivExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SDIV, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvUnsignedRemExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_UREM, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedRemExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SREM, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedModExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SMOD, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvUnsignedLessExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_ULT, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedLessExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SLT, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvUnsignedLessOrEqualExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_ULE, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedLessOrEqualExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SLE, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvUnsignedGreaterOrEqualExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_UGE, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedGreaterOrEqualExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SGE, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvUnsignedGreaterExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_UGT, arg0, arg1)
        }
    }

    override fun  transform(expr: KBvSignedGreaterExpr) = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_SGT, arg0, arg1)
        }
    }

    override fun transform(expr: KBvConcatExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.BITVECTOR_CONCAT, arg0, arg1)
        }
    }

    override fun transform(expr: KBvExtractExpr) = with(expr) {
        transform(value) { value: Term ->
            val extractOp = tm.mkOp(Kind.BITVECTOR_EXTRACT, high, low)
            tm.mkTerm(extractOp, value)
        }
    }

    override fun transform(expr: KBvSignExtensionExpr) = with(expr) {
        transform(value) { value: Term ->
            val extensionOp = tm.mkOp(Kind.BITVECTOR_SIGN_EXTEND, extensionSize)
            tm.mkTerm(extensionOp, value)
        }
    }

    override fun transform(expr: KBvZeroExtensionExpr) = with(expr) {
        transform(value) { value: Term ->
            val extensionOp = tm.mkOp(Kind.BITVECTOR_ZERO_EXTEND, extensionSize)
            tm.mkTerm(extensionOp, value)
        }
    }

    override fun transform(expr: KBvRepeatExpr) = with(expr) {
        transform(value) { value: Term ->
            val repeatOp = tm.mkOp(Kind.BITVECTOR_REPEAT, repeatNumber)
            tm.mkTerm(repeatOp, value)
        }
    }

    override fun  transform(expr: KBvShiftLeftExpr) = with(expr) {
        transform(arg, shift) { arg: Term, shift: Term ->
            tm.mkTerm(Kind.BITVECTOR_SHL, arg, shift)
        }
    }

    override fun  transform(expr: KBvLogicalShiftRightExpr) = with(expr) {
        transform(arg, shift) { arg: Term, shift: Term ->
            tm.mkTerm(Kind.BITVECTOR_LSHR, arg, shift)
        }
    }

    override fun  transform(expr: KBvArithShiftRightExpr) = with(expr) {
        transform(arg, shift) { arg: Term, shift: Term ->
            tm.mkTerm(Kind.BITVECTOR_ASHR, arg, shift)
        }
    }

    /*
     * we can internalize rotate expr as concat expr due to simplification,
     * otherwise we can't process rotate expr on expression
     */
    override fun  transform(expr: KBvRotateLeftExpr) = with(expr) {
        transform {
            val simplifiedExpr = ctx.simplifyBvRotateLeftExpr(arg, rotation)

            if (simplifiedExpr is KBvRotateLeftExpr<*>) {
                throw KSolverUnsupportedFeatureException(
                    "Rotate expr with expression argument is not supported by cvc5"
                )
            }

            simplifiedExpr.internalizeExpr()
        }
    }

    /*
     * @see transform(expr: KBvRotateLeftExpr)
     */
    override fun  transform(expr: KBvRotateRightExpr) = with(expr) {
        transform {
            val simplifiedExpr = ctx.simplifyBvRotateRightExpr(arg, rotation)

            if (simplifiedExpr is KBvRotateRightExpr<*>) {
                throw KSolverUnsupportedFeatureException(
                    "Rotate expr with expression argument is not supported by cvc5"
                )
            }

            simplifiedExpr.internalizeExpr()
        }
    }

    override fun  transform(expr: KBvRotateLeftIndexedExpr) = with(expr) {
        transform(value) { value: Term ->
            val rotationOp = tm.mkOp(Kind.BITVECTOR_ROTATE_LEFT, rotationNumber)
            tm.mkTerm(rotationOp, value)
        }
    }


    override fun  transform(expr: KBvRotateRightIndexedExpr) = with(expr) {
        transform(value) { value: Term ->
            val rotationOp = tm.mkOp(Kind.BITVECTOR_ROTATE_RIGHT, rotationNumber)
            tm.mkTerm(rotationOp, value)
        }
    }

    // custom implementation
    @Suppress("MagicNumber")
    override fun transform(expr: KBv2IntExpr) = with(expr) {
        transform(value) { value: Term ->
            // by default, it is unsigned in cvc5
            val intTerm = tm.mkTerm(Kind.BITVECTOR_TO_NAT, value)

            if (isSigned) {
                val size = this.value.sort.sizeBits.toInt()
                val modulo = powerOfTwo(size.toUInt())
                val maxInt = (powerOfTwo((size - 1).toUInt())) - BigInteger.ONE

                val moduloTerm = tm.builder { mkInteger(modulo.toString(10)) }
                val maxIntTerm = tm.builder { mkInteger(maxInt.toString(10)) }

                val gtTerm = tm.mkTerm(Kind.GT, intTerm, maxIntTerm)
                val subTerm = tm.mkTerm(Kind.SUB, intTerm, moduloTerm)

                tm.mkTerm(Kind.ITE, gtTerm, subTerm, intTerm)
            } else intTerm
        }
    }

    override fun  transform(expr: KBvAddNoOverflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvAddNoOverflowExpr(arg0, arg1, isSigned).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvAddNoUnderflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvAddNoUnderflowExpr(arg0, arg1).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvSubNoOverflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvSubNoOverflowExpr(arg0, arg1).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvSubNoUnderflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvSubNoUnderflowExpr(arg0, arg1, isSigned).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvDivNoOverflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvDivNoOverflowExpr(arg0, arg1).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvNegNoOverflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvNegNoOverflowExpr(value).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvMulNoOverflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvMulNoOverflowExpr(arg0, arg1, isSigned).internalizeExpr()
        }
    }

    override fun  transform(expr: KBvMulNoUnderflowExpr) = with(expr) {
        transform {
            ctx.rewriteBvMulNoUnderflowExpr(arg0, arg1).internalizeExpr()
        }
    }

    private fun fpToBvTerm(signBit: Boolean, biasedExp: KBitVecValue<*>, significand: KBitVecValue<*>): Term {
        val signString = if (signBit) "1" else "0"
        val expString = biasedExp.stringValue
        val significandString = significand.stringValue

        val bvString = signString + expString + significandString

        return tm.builder { mkBitVector(bvString.length, bvString, 2) }
    }

    private fun  KFpValue.toBitvectorTerm(): Term = fpToBvTerm(signBit, biasedExponent, significand)

    private fun > transformFpValue(expr: T): T = with(expr) {
        transform {
            val bv = expr.toBitvectorTerm()
            // created from IEEE-754 bit-vector representation of the floating-point value
            tm.builder {
                mkFloatingPoint(sort.exponentBits.toInt(), sort.significandBits.toInt(), bv)
            }
        }
    }

    override fun transform(expr: KFp16Value): KExpr = transformFpValue(expr)

    override fun transform(expr: KFp32Value): KExpr = transformFpValue(expr)

    override fun transform(expr: KFp64Value): KExpr = transformFpValue(expr)

    override fun transform(expr: KFp128Value): KExpr = transformFpValue(expr)

    override fun transform(expr: KFpCustomSizeValue): KExpr = transformFpValue(expr)

    override fun transform(expr: KFpRoundingModeExpr): KExpr = with(expr) {
        transform {
            val rmMode = when (value) {
                KFpRoundingMode.RoundNearestTiesToEven -> RoundingMode.ROUND_NEAREST_TIES_TO_EVEN
                KFpRoundingMode.RoundNearestTiesToAway -> RoundingMode.ROUND_NEAREST_TIES_TO_AWAY
                KFpRoundingMode.RoundTowardPositive -> RoundingMode.ROUND_TOWARD_POSITIVE
                KFpRoundingMode.RoundTowardNegative -> RoundingMode.ROUND_TOWARD_NEGATIVE
                KFpRoundingMode.RoundTowardZero -> RoundingMode.ROUND_TOWARD_ZERO
            }

            tm.builder { mkRoundingMode(rmMode) }
        }
    }

    override fun  transform(expr: KFpAbsExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_ABS, value)
        }
    }

    override fun  transform(expr: KFpNegationExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_NEG, value)
        }
    }

    override fun  transform(expr: KFpAddExpr): KExpr = with(expr) {
        transform(roundingMode, arg0, arg1) { roundingMode: Term, arg0: Term, arg1: Term ->
            tm.mkTerm(
                Kind.FLOATINGPOINT_ADD,
                roundingMode,
                arg0,
                arg1
            )
        }
    }

    override fun  transform(expr: KFpSubExpr): KExpr = with(expr) {
        transform(roundingMode, arg0, arg1) { roundingMode: Term, arg0: Term, arg1: Term ->
            tm.mkTerm(
                Kind.FLOATINGPOINT_SUB,
                roundingMode,
                arg0,
                arg1
            )
        }
    }

    override fun  transform(expr: KFpMulExpr): KExpr = with(expr) {
        transform(roundingMode, arg0, arg1) { roundingMode: Term, arg0: Term, arg1: Term ->
            tm.mkTerm(
                Kind.FLOATINGPOINT_MULT,
                roundingMode,
                arg0,
                arg1
            )
        }
    }

    override fun  transform(expr: KFpDivExpr): KExpr = with(expr) {
        transform(roundingMode, arg0, arg1) { roundingMode: Term, arg0: Term, arg1: Term ->
            tm.mkTerm(
                Kind.FLOATINGPOINT_DIV,
                roundingMode,
                arg0,
                arg1
            )
        }
    }

    override fun  transform(expr: KFpFusedMulAddExpr): KExpr = with(expr) {
        transform(roundingMode, arg0, arg1, arg2) { rm: Term, arg0: Term, arg1: Term, arg2: Term ->
            tm.mkTerm(
                Kind.FLOATINGPOINT_FMA,
                arrayOf(rm, arg0, arg1, arg2)
            )
        }
    }

    override fun  transform(expr: KFpSqrtExpr): KExpr = with(expr) {
        transform(roundingMode, value) { roundingMode: Term, value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_SQRT, roundingMode, value)
        }
    }

    override fun  transform(expr: KFpRemExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_REM, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpRoundToIntegralExpr): KExpr =
        with(expr) {
            transform(roundingMode, value) { roundingMode: Term, value: Term ->
                tm.mkTerm(Kind.FLOATINGPOINT_RTI, roundingMode, value)
            }
        }

    override fun  transform(expr: KFpMinExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_MIN, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpMaxExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_MAX, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpLessOrEqualExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_LEQ, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpLessExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_LT, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpGreaterOrEqualExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_GEQ, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpGreaterExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_GT, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpEqualExpr): KExpr = with(expr) {
        transform(arg0, arg1) { arg0: Term, arg1: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_EQ, arg0, arg1)
        }
    }

    override fun  transform(expr: KFpIsNormalExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_NORMAL, value)
        }
    }

    override fun  transform(expr: KFpIsSubnormalExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_SUBNORMAL, value)
        }
    }

    override fun  transform(expr: KFpIsZeroExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_ZERO, value)
        }
    }

    override fun  transform(expr: KFpIsInfiniteExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_INF, value)
        }
    }

    override fun  transform(expr: KFpIsNaNExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_NAN, value)
        }
    }

    override fun  transform(expr: KFpIsNegativeExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_NEG, value)
        }
    }

    override fun  transform(expr: KFpIsPositiveExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_IS_POS, value)
        }
    }

    override fun  transform(expr: KFpToBvExpr): KExpr =
        with(expr) {
            transform(roundingMode, value) { rm: Term, value: Term ->
                val opKind = if (isSigned) Kind.FLOATINGPOINT_TO_SBV else Kind.FLOATINGPOINT_TO_UBV
                val op = tm.mkOp(opKind, bvSize)
                tm.mkTerm(op, rm, value)
            }
        }

    override fun  transform(expr: KFpToRealExpr): KExpr = with(expr) {
        transform(value) { value: Term ->
            tm.mkTerm(Kind.FLOATINGPOINT_TO_REAL, value)
        }
    }

    override fun  transform(expr: KFpToIEEEBvExpr): KExpr =
        throw KSolverUnsupportedFeatureException("no direct support for $expr")

    override fun  transform(expr: KFpFromBvExpr): KExpr = with(expr) {
        transform(sign, biasedExponent, significand) { sign: Term, biasedExp: Term, significand: Term ->
            val bvTerm = tm.mkTerm(
                Kind.BITVECTOR_CONCAT,
                tm.mkTerm(Kind.BITVECTOR_CONCAT, sign, biasedExp),
                significand
            )

            val toFpOp = tm.mkOp(
                Kind.FLOATINGPOINT_TO_FP_FROM_IEEE_BV,
                sort.exponentBits.toInt(),
                sort.significandBits.toInt()
            )

            tm.mkTerm(toFpOp, bvTerm)
        }
    }

    override fun  transform(expr: KFpToFpExpr): KExpr = with(expr) {
        transform(roundingMode, value) { rm: Term, value: Term ->
            val op = tm.mkOp(
                Kind.FLOATINGPOINT_TO_FP_FROM_FP,
                sort.exponentBits.toInt(),
                sort.significandBits.toInt()
            )

            tm.mkTerm(op, rm, value)
        }
    }

    override fun  transform(expr: KRealToFpExpr): KExpr = with(expr) {
        transform(roundingMode, value) { rm: Term, value: Term ->
            val op = tm.mkOp(
                Kind.FLOATINGPOINT_TO_FP_FROM_REAL,
                sort.exponentBits.toInt(),
                sort.significandBits.toInt()
            )

            tm.mkTerm(op, rm, value)
        }
    }

    override fun  transform(expr: KBvToFpExpr): KExpr = with(expr) {
        transform(roundingMode, value) { rm: Term, value: Term ->
            val opKind = if (signed) Kind.FLOATINGPOINT_TO_FP_FROM_SBV else Kind.FLOATINGPOINT_TO_FP_FROM_UBV
            val op = tm.mkOp(
                opKind,
                sort.exponentBits.toInt(),
                sort.significandBits.toInt()
            )

            tm.mkTerm(op, rm, value)
        }
    }

    override fun  transform(expr: KArrayStore) =
        expr.transformStore()

    override fun  transform(
        expr: KArray2Store
    ): KExpr> = expr.transformStore()

    override fun  transform(
        expr: KArray3Store
    ): KExpr> = expr.transformStore()

    override fun  transform(expr: KArrayNStore): KExpr> =
        expr.transformStore()

    private fun > E.transformStore(): E =
        transformArray(listOf(array, value) + indices) { transformedArgs: Array ->
            val (array, value) = transformedArgs.take(2)
            val indices = transformedArgs.drop(2)
            mkArrayStoreTerm(array, indices, value)
        }

    override fun  transform(expr: KArraySelect) =
        expr.transformSelect()

    override fun  transform(expr: KArray2Select) =
        expr.transformSelect()

    override fun  transform(
        expr: KArray3Select
    ): KExpr = expr.transformSelect()

    override fun  transform(expr: KArrayNSelect): KExpr =
        expr.transformSelect()

    private fun > E.transformSelect(): E =
        transformArray(listOf(array) + indices) { transformedArgs: Array ->
            val array = transformedArgs.first()
            val indices = transformedArgs.drop(1)
            mkArraySelectTerm(array, indices)
        }

    override fun , R : KSort> transform(expr: KArrayConst) = with(expr) {
        transform(value) { valueTerm: Term ->
            if (value is KInterpretedValue<*> || value is KArrayConst<*, *>) {
                // const array base must be a value or a constant array
                tm.builder { mkConstArray(sort.internalizeSort(), valueTerm) }
            } else {
                val bounds = sort.domainSorts.map { tm.builder { mkConst(it.internalizeSort()) } }
                mkLambdaTerm(bounds, valueTerm)
            }
        }
    }

    override fun  transform(expr: KArrayLambda) =
        expr.transformLambda()

    override fun  transform(
        expr: KArray2Lambda
    ): KExpr> =
        expr.transformLambda()

    override fun  transform(
        expr: KArray3Lambda
    ): KExpr> =
        expr.transformLambda()

    override fun  transform(expr: KArrayNLambda): KExpr> =
        expr.transformLambda()

    private fun > E.transformLambda(): E = transform(body) { bodyTerm: Term ->
        val bounds = indexVarDeclarations.map { it.internalizeDecl() }
        mkLambdaTerm(bounds, bodyTerm)
    }

    override fun  transform(expr: KAddArithExpr) = with(expr) {
        transformArray(args) { args -> tm.mkTerm(Kind.ADD, args) }
    }

    override fun  transform(expr: KSubArithExpr) = with(expr) {
        transformArray(args) { args -> tm.mkTerm(Kind.SUB, args) }
    }

    override fun  transform(expr: KMulArithExpr) = with(expr) {
        transformArray(args) { args -> tm.mkTerm(Kind.MULT, args) }
    }

    override fun  transform(expr: KUnaryMinusArithExpr) = with(expr) {
        transform(arg) { arg: Term -> tm.mkTerm(Kind.NEG, arg) }
    }

    override fun  transform(expr: KDivArithExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            arithDivide(sort, lhs, rhs)
        }
    }

    private fun arithDivide(sort: KArithSort, lhs: Term, rhs: Term): Term = with(sort.ctx) {
        when (sort) {
            realSort -> tm.mkTerm(Kind.DIVISION, lhs, rhs)
            intSort -> tm.mkTerm(Kind.INTS_DIVISION, lhs, rhs)
            else -> throw KSolverUnsupportedFeatureException("Arith sort $sort is unsupported")
        }
    }

    override fun  transform(expr: KPowerArithExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            /**
             * According to the cvc5 docs:
             * The exponent of the POW(^) operator can only be a positive integral constant below 67108864
             * */
            val exponentMaxValue = 67108864.toBigInteger()
            if (!rhs.isIntegerValue || rhs.realOrIntegerValueSign < 0 || rhs.integerValue > exponentMaxValue) {
                throw KSolverUnsupportedFeatureException(
                    "The exponent of the $expr can only be a positive integral constant below 67108864"
                )
            }

            tm.mkTerm(Kind.POW, lhs, rhs)
        }
    }

    override fun  transform(expr: KLtArithExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            tm.mkTerm(Kind.LT, lhs, rhs)
        }
    }

    override fun  transform(expr: KLeArithExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            tm.mkTerm(Kind.LEQ, lhs, rhs)
        }
    }

    override fun  transform(expr: KGtArithExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            tm.mkTerm(Kind.GT, lhs, rhs)
        }
    }

    override fun  transform(expr: KGeArithExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            tm.mkTerm(Kind.GEQ, lhs, rhs)
        }
    }

    override fun transform(expr: KModIntExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            tm.mkTerm(Kind.INTS_MODULUS, lhs, rhs)
        }
    }

    // custom implementation
    override fun transform(expr: KRemIntExpr) = with(expr) {
        transform(lhs, rhs) { lhs: Term, rhs: Term ->
            // there is no ints remainder in cvc5
            val remSign = tm.mkTerm(Kind.GEQ, lhs, zeroIntValueTerm)
                .xorTerm(tm.mkTerm(Kind.GEQ, rhs, zeroIntValueTerm))
            val modTerm = tm.mkTerm(Kind.INTS_MODULUS, lhs, rhs)

            remSign.iteTerm(tm.mkTerm(Kind.NEG, modTerm), modTerm)
        }
    }

    override fun transform(expr: KToRealIntExpr) = with(expr) {
        transform(arg) { arg: Term ->
            tm.mkTerm(Kind.TO_REAL, arg)
        }
    }

    override fun transform(expr: KInt32NumExpr) = with(expr) {
        transform { tm.builder { mkInteger(expr.value.toLong()) } }
    }

    override fun transform(expr: KInt64NumExpr) = with(expr) {
        // We need to pass String value here because on Windows it might be cut to 32 bit int value
        transform { tm.builder { mkInteger(expr.value.toString()) } }
    }

    override fun transform(expr: KIntBigNumExpr) = with(expr) {
        transform { tm.builder { mkInteger(expr.value.toString()) } }
    }

    override fun transform(expr: KToIntRealExpr) = with(expr) {
        transform(arg) { arg: Term ->
            tm.mkTerm(Kind.TO_INTEGER, arg)
        }
    }

    override fun transform(expr: KIsIntRealExpr) = with(expr) {
        transform(arg) { arg: Term ->
            tm.mkTerm(Kind.IS_INTEGER, arg)
        }
    }

    override fun transform(expr: KRealNumExpr) = with(expr) {
        transform(numerator, denominator) { num: Term, denom: Term ->
            val numAsReal = tm.mkTerm(Kind.TO_REAL, num)
            val denomAsReal = tm.mkTerm(Kind.TO_REAL, denom)

            tm.mkTerm(Kind.DIVISION, numAsReal, denomAsReal)
        }
    }

    override fun transform(expr: KExistentialQuantifier) =
        expr.transformQuantifiedExpression(expr.bounds, expr.body, isUniversal = false)

    override fun transform(expr: KUniversalQuantifier) =
        expr.transformQuantifiedExpression(expr.bounds, expr.body, isUniversal = true)

    override fun , R : KSort> transform(expr: KFunctionAsArray): KExpr {
        throw KSolverUnsupportedFeatureException(
            "No direct impl in cvc5 (as-array is CONST_ARRAY term with base array element)"
        )
    }

    /**
     * There is no way in cvc5 API to mark uninterpreted constant as value.
     *
     * To overcome this we apply the following scheme:
     * 1. Internalize value `x` of a sort T as normal constant.
     * 2. Associate unique interpreted value `i` with this constant.
     * Currently, we use integer values.
     * 3. Introduce `interpreter` function `F` of type T -> Int.
     * We introduce one function for each uninterpreted sort.
     * 4. Assert expression `(= i (F x))` to the solver.
     * Since all Int values are known to be distinct, this
     * assertion forces that all values of T are also distinct.
     * */
    override fun transform(expr: KUninterpretedSortValue): KExpr = with(expr) {
        transform(ctx.mkIntNum(expr.valueIdx)) { intValueExpr: Term ->
            val exprSort = sort.internalizeSort()
            cvc5Ctx.saveUninterpretedSortValue(tm.builder { mkConst(exprSort) }, expr).also {
                cvc5Ctx.registerUninterpretedSortValue(expr, intValueExpr, it) {
                    val descriptorSort = ctx.intSort.internalizeSort()
                    solver.declareFun("${sort.name}!interpreter", arrayOf(exprSort), descriptorSort)
                        .also { f -> tm.registerPointer(f) }
                }
            }
        }
    }

    private fun > E.transformQuantifiedExpression(
        bounds: List>,
        body: KExpr<*>,
        isUniversal: Boolean
    ): E = transform(body) { transformedBody: Term ->
        val boundConsts = bounds.map { it.internalizeDecl() }
        mkQuantifierTerm(isUniversal, boundConsts, transformedBody)
    }

    private fun mkQuantifierTerm(isUniversal: Boolean, bounds: List, body: Term): Term =
        resolveBoundVars(bounds, body) { boundVars, bodyWithVars ->
            tm.mkQuantifier(isUniversal, boundVars, bodyWithVars)
        }

    private fun mkLambdaTerm(bounds: List, body: Term): Term =
        resolveBoundVars(bounds, body) { boundVars, bodyWithVars ->
            tm.mkLambda(boundVars, bodyWithVars)
        }

    private inline fun resolveBoundVars(bounds: List, body: Term, mkTerm: (Array, Term) -> Term): Term {
        val boundVars = bounds.map {
            val sort = tm.termSort(it)
            if (it.hasSymbol()) {
                tm.builder { mkVar(sort, it.symbol) }
            } else {
                tm.builder { mkVar(sort) }
            }
        }.toTypedArray()

        val bodyWithVars = body.substitute(bounds.toTypedArray(), boundVars)
        return mkTerm(boundVars, bodyWithVars)
    }

    private fun Term.mkFunctionApp(args: List): Term =
        tm.mkTerm(Kind.APPLY_UF, arrayOf(this) + args)

    private fun mkAndTerm(args: List): Term = when (args.size) {
        0 -> tm.builder { mkTrue() }
        1 -> args.single()
        else -> tm.mkTerm(Kind.AND, args.toTypedArray())
    }

    private fun mkOrTerm(args: List): Term = when (args.size) {
        0 -> tm.builder { mkFalse() }
        1 -> args.single()
        else -> tm.mkTerm(Kind.OR, args.toTypedArray())
    }

    private fun mkArraySelectTerm(array: Term, indices: List): Term =
        if (tm.termSort(array).isArray) {
            val selectIdx = mkArrayOperationIndex(indices)
            tm.mkTerm(Kind.SELECT, array, selectIdx)
        } else {
            tm.mkTerm(Kind.APPLY_UF, arrayOf(array) + indices)
        }

    private fun mkArrayStoreTerm(array: Term, indices: List, value: Term): Term =
        if (tm.termSort(array).isArray) {
            val storeIdx = mkArrayOperationIndex(indices)
            tm.mkTerm(Kind.STORE, arrayOf(array, storeIdx, value))
        } else {
            val boundVars = indices.map { tm.builder { mkVar(tm.termSort(it)) } }

            val condition = indices.zip(boundVars) { idx, boundVar ->
                idx.eqTerm(boundVar)
            }.let { mkAndTerm(it) }

            val arrayValue = array.mkFunctionApp(boundVars)
            val body = condition.iteTerm(value, arrayValue)

            tm.mkLambda(boundVars.toTypedArray(), body)
        }

    private fun mkArrayOperationIndex(indices: List): Term =
        if (indices.size == 1) {
            indices.single()
        } else {
            tm.builder { mkTuple(indices.toTypedArray()) }
        }

    private fun mkArrayEqTerm(arraySort: KArraySortBase<*>, lhs: Term, rhs: Term): Term {
        if (tm.termSort(lhs) == tm.termSort(rhs)) {
            return lhs.eqTerm(rhs)
        }

        val leftLambda = ensureArrayLambda(arraySort, lhs)
        val rightLambda = ensureArrayLambda(arraySort, rhs)
        return leftLambda.eqTerm(rightLambda)
    }

    /**
     * Ensure that an array expression has a function type.
     *
     * Since arrays can be represented as `SMT arrays` or as functions (lambda expressions)
     * we must coerce them to the same type.
     * We coerce `SMT arrays` to functions.
     * */
    private fun ensureArrayLambda(arraySort: KArraySortBase<*>, term: Term): Term =
        if (!tm.termSort(term).isArray) {
            term
        } else {
            val boundVars = arraySort.domainSorts.map { tm.builder { mkVar(it.internalizeSort()) } }
            val body = mkArraySelectTerm(term, boundVars)
            tm.mkLambda(boundVars.toTypedArray(), body)
        }

    private fun mkArrayIteTerm(arraySort: KArraySortBase<*>, cond: Term, trueBranch: Term, falseBranch: Term): Term {
        if (tm.termSort(trueBranch).isArray && tm.termSort(falseBranch).isArray) {
            return cond.iteTerm(trueBranch, falseBranch)
        }

        val boundVars = arraySort.domainSorts.map { tm.builder { mkVar(it.internalizeSort()) } }
        val trueValue = mkArraySelectTerm(trueBranch, boundVars)
        val falseValue = mkArraySelectTerm(falseBranch, boundVars)

        val body = cond.iteTerm(trueValue, falseValue)

        return tm.mkLambda(boundVars.toTypedArray(), body)
    }

    private fun mkArrayDistinctTerm(arraySort: KArraySortBase<*>, args: List): Term {
        val inequalities = mutableListOf()
        for (i in args.indices) {
            for (j in (i + 1) until args.size) {
                val equality = mkArrayEqTerm(arraySort, args[i], args[j])
                inequalities.add(equality.notTerm())
            }
        }

        return mkAndTerm(inequalities)
    }

    private inline fun mkArraySpecificTerm(
        sort: KSort,
        arrayTerm: (KArraySortBase<*>) -> Term,
        default: () -> Term
    ): Term = if (sort is KArraySortBase<*>) {
        arrayTerm(sort)
    } else {
        default()
    }

    inline fun > S.transformArray(
        args: List>,
        operation: (Array) -> Term
    ): S = transformList(args) { a: Array -> operation(a) }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy