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

commonMain.it.unibo.tuprolog.solve.streams.stdlib.primitive.Conjunction.kt Maven / Gradle / Ivy

package it.unibo.tuprolog.solve.streams.stdlib.primitive

import it.unibo.tuprolog.core.Substitution
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.Tuple
import it.unibo.tuprolog.solve.ExecutionContext
import it.unibo.tuprolog.solve.Solution
import it.unibo.tuprolog.solve.forEachWithLookahead
import it.unibo.tuprolog.solve.primitive.PrimitiveWrapper
import it.unibo.tuprolog.solve.primitive.Solve
import it.unibo.tuprolog.solve.sideffects.SideEffect
import it.unibo.tuprolog.solve.streams.StreamsSolver
import it.unibo.tuprolog.solve.streams.solver.SideEffectManagerImpl
import it.unibo.tuprolog.solve.streams.solver.StreamsExecutionContext
import it.unibo.tuprolog.solve.streams.solver.addWithNoDuplicates
import it.unibo.tuprolog.solve.streams.solver.moreThanOne
import it.unibo.tuprolog.solve.streams.solver.newSolveRequest
import it.unibo.tuprolog.solve.streams.solver.orderWithStrategy
import it.unibo.tuprolog.solve.streams.solver.prepareForExecutionAsGoal
import it.unibo.tuprolog.solve.streams.solver.replyWith
import it.unibo.tuprolog.solve.streams.solver.shouldExecuteThrowCut

/**
 * Implementation of primitive handling `','/2` behaviour
 *
 * @author Enrico
 */
internal object Conjunction : PrimitiveWrapper(Tuple.FUNCTOR, 2) {
    override fun uncheckedImplementation(request: Solve.Request): Sequence =
        sequence {
            val subGoals =
                with(request) {
                    query.castToTuple().toSequence()
                        .orderWithStrategy(context, context.solverStrategies::predicationChoiceStrategy)
                        .toList()
                }

            solveConjunctionGoals(
                request,
                subGoals,
                request.context,
                Substitution.empty(),
                emptyList(),
                request.context.sideEffectManager,
                previousGoalsHadAlternatives = false,
            )
        }

    /**
     * This utility method implements the conjunction solving procedure
     *
     * @param mainRequest The initial conjunction request with all goals, to be referred as the only logical parent
     * @param goals The goals in conjunction together, to be solved left to right
     * @param accumulatedSubstitutions This is a substitution accumulator, that maintains the diff from the [mainRequest] substitution
     * @param previousResponseSideEffectManager This is the previous response side effect manager, needed to propagate information during execution
     * @param previousGoalsHadAlternatives This signals if previous conjunction goals had unexplored alternatives
     *
     * @return The boolean value signaling if the following goals executed the cut or not
     */
    private suspend fun SequenceScope.solveConjunctionGoals(
        mainRequest: Solve.Request,
        goals: Iterable,
        toPropagateContext: ExecutionContext,
        accumulatedSubstitutions: Substitution,
        accumulatedSideEffects: List,
        previousResponseSideEffectManager: SideEffectManagerImpl?,
        previousGoalsHadAlternatives: Boolean,
    ): Pair> {
        val goal = goals.first().apply(accumulatedSubstitutions).prepareForExecutionAsGoal()

        val goalRequest =
            mainRequest.newSolveRequest(
                goal,
                accumulatedSubstitutions,
                toPropagateContext,
                baseSideEffectManager = previousResponseSideEffectManager ?: mainRequest.context.sideEffectManager,
            )

        var cutExecuted = false
        var currentSideEffects = emptyList()
        StreamsSolver.solveToFinalStates(goalRequest).forEachWithLookahead { goalFinalState, currentHasAlternatives ->
            val goalResponse = goalFinalState.solve
            if (Cut.functor == goal.functor || goalResponse.sideEffectManager?.shouldExecuteThrowCut() == true) {
                cutExecuted = true
            }

            currentSideEffects = currentSideEffects.addWithNoDuplicates(goalResponse.sideEffects)

            when {
                goalResponse.sideEffectManager?.shouldExecuteThrowCut() == false &&
                    goalResponse.solution is Solution.Yes &&
                    moreThanOne(goals.asSequence()) -> {
                    val sideEffectPair =
                        solveConjunctionGoals(
                            mainRequest,
                            goals.drop(1),
                            goalFinalState.context.apply(currentSideEffects),
                            goalResponse.solution.substitution - mainRequest.context.substitution,
                            currentSideEffects,
                            goalResponse.sideEffectManager as? SideEffectManagerImpl,
                            previousGoalsHadAlternatives || currentHasAlternatives,
                        )

                    if (sideEffectPair.first) {
                        cutExecuted = true
                    }

                    currentSideEffects = sideEffectPair.second
                }
                else ->
                    // yield only non-false responses or false responses when there are no open alternatives (because no more or cut)
                    if (
                        goalResponse.solution !is Solution.No ||
                        (!previousGoalsHadAlternatives && !currentHasAlternatives) ||
                        cutExecuted
                    ) {
                        yield(
                            mainRequest.replyWith(
                                goalResponse.copy(sideEffects = accumulatedSideEffects + currentSideEffects),
                            ),
                        )
                    }
            }
            // cut other alternatives of current and previous goals
            if (cutExecuted || goalResponse.solution is Solution.Halt) {
                return Pair(true, accumulatedSideEffects + currentSideEffects)
            }
        }
        return Pair(cutExecuted, accumulatedSideEffects + currentSideEffects)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy