<commit_before>package com.github.kerubistan.kerub.planner

import com.github.k0zka.finder4j.backtrack.StepFactory
import com.github.kerubistan.kerub.planner.issues.problems.CompositeProblemDetectorImpl
import com.github.kerubistan.kerub.planner.issues.problems.Problem
import com.github.kerubistan.kerub.planner.issues.problems.ProblemDetector
import com.github.kerubistan.kerub.planner.steps.AbstractOperationalStep
import com.github.kerubistan.kerub.planner.steps.InvertibleStep
import com.github.kerubistan.kerub.utils.LogLevel
import com.github.kerubistan.kerub.utils.silent

class PlanRationalizerImpl(
		private val stepFactory: StepFactory<AbstractOperationalStep, Plan>,
		private val violationDetector: PlanViolationDetector = PlanViolationDetectorImpl,
		private val problemDetector: ProblemDetector<Problem> = CompositeProblemDetectorImpl
) : PlanRationalizer {

	/**
	 * Logic behind the rationalization process:
	 * - try to remove pairs of inverse steps (they recognize each other) - done
	 * - try to remove single steps and check if they still solve the problem - done
	 * - but this is not good enough because maybe we have sequences of irrelevant steps - TODO
	 */
	override fun rationalize(plan: Plan): Plan =
			if (plan.steps.size > 1) {
				val cleanup = trySimilar(tryRemoveSingles(tryRemoveInverses(plan)))
				(1..(cleanup.steps.size - 1)).map {
					subPlan(cleanup, it)
				}.filterNotNull().minBy { it.steps.size } ?: cleanup
			} else plan

	fun simplify(plan: Plan, generator : (OperationalState, List<AbstractOperationalStep>) -> Plan?) : Plan =
			if(plan.steps.size <= 1) {
				plan
			} else {
				var work = plan

				val initialState = plan.states.first()
				plan.steps.forEach {
					step ->

					val candidatePlan = generator(initialState, work.steps - step)
					if(candidatePlan != null && isTargetState(candidatePlan)) {
						work = candidatePlan
					}

				}
				work
			}

	private fun trySimilar(plan: Plan): Plan = simplify(plan, this::createSimilarPlan)

	private fun tryRemoveSingles(plan: Plan): Plan = simplify(plan, this::createStrictPlan)

	private fun createSimilarPlan(initial: OperationalState, steps: List<AbstractOperationalStep>): Plan? {
		var work = Plan(initial, listOf())
		steps.forEach{
			step ->
			val offeredSteps = stepFactory.produce(work)
			if(step.javaClass in offeredSteps.map { it.javaClass }) {
				work = Plan.planBy(initial, work.steps + step)
			} else {
				return null
			}
		}
		return work
	}

	private fun createStrictPlan(initial: OperationalState, steps: List<AbstractOperationalStep>): Plan? {
		var work = Plan(initial, listOf())
		steps.forEach{
			step ->
			val offeredSteps = stepFactory.produce(work)
			if(step in offeredSteps) {
				work = Plan.planBy(initial, work.steps + step)<commit_msg>PlanRationalizerImpl review and simplification<commit_after>package com.github.kerubistan.kerub.planner

import com.github.k0zka.finder4j.backtrack.StepFactory
import com.github.kerubistan.kerub.planner.issues.problems.CompositeProblemDetectorImpl
import com.github.kerubistan.kerub.planner.issues.problems.Problem
import com.github.kerubistan.kerub.planner.issues.problems.ProblemDetector
import com.github.kerubistan.kerub.planner.steps.AbstractOperationalStep
import com.github.kerubistan.kerub.planner.steps.InvertibleStep
import com.github.kerubistan.kerub.utils.LogLevel
import com.github.kerubistan.kerub.utils.silent

class PlanRationalizerImpl(
		private val stepFactory: StepFactory<AbstractOperationalStep, Plan>,
		private val violationDetector: PlanViolationDetector = PlanViolationDetectorImpl,
		private val problemDetector: ProblemDetector<Problem> = CompositeProblemDetectorImpl
) : PlanRationalizer {

	/**
	 * Logic behind the rationalization process:
	 * - try to remove pairs of inverse steps (they recognize each other) - done
	 * - try to remove single steps and check if they still solve the problem - done
	 */
	override fun rationalize(plan: Plan): Plan =
			if (plan.steps.size > 1) {
				val cleanup = tryRemoveSingles(tryRemoveInverses(plan))
				(1..(cleanup.steps.size - 1)).map {
					subPlan(cleanup, it)
				}.filterNotNull().minBy { it.steps.size } ?: cleanup
			} else plan

	private fun simplify(plan: Plan, generator: (OperationalState, List<AbstractOperationalStep>) -> Plan?): Plan =
			if (plan.steps.size <= 1) {
				plan
			} else {
				var work = plan

				val initialState = plan.states.first()
				plan.steps.forEach { step ->

					val candidatePlan = generator(initialState, work.steps - step)
					if (candidatePlan != null && isTargetState(candidatePlan)) {
						work = candidatePlan
					}

				}
				work
			}

	private fun tryRemoveSingles(plan: Plan): Plan = simplify(plan, this::createStrictPlan)

	private fun createStrictPlan(initial: OperationalState, steps: List<AbstractOperationalStep>): Plan? {
		var work = Plan(initial, listOf())
		steps.forEach { step ->
			val offeredSteps = stepFactory.produce(work)
			if (step in offeredSteps) {
				work = Plan.planBy(initial, work.steps + step)
{'fraction_non_alphanumeric': 0.10138888888888889, 'fraction_numerical': 0.002380952380952381, 'mean_word_length': 4.3627659574468085, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6989900', 'n_tokens_mistral': 1729, 'n_tokens_neox': 1507, 'n_words': 353}