<commit_before>package com.planetmayo.debrief.satc.model.contributions;

import java.util.ArrayList;
import java.util.Iterator;

import com.planetmayo.debrief.satc.model.VehicleType;
import com.planetmayo.debrief.satc.model.states.BaseRange.IncompatibleStateException;
import com.planetmayo.debrief.satc.model.states.BoundedState;
import com.planetmayo.debrief.satc.model.states.CourseRange;
import com.planetmayo.debrief.satc.model.states.ProblemSpace;

public class CourseAnalysisContribution extends BaseContribution
{
	private static final long serialVersionUID = 1L;

	public CourseAnalysisContribution()
	{
		super();
		setName("Course Analysis");
	}

	@Override
	public void actUpon(ProblemSpace space) throws IncompatibleStateException
	{
		// remember the previous state
		BoundedState lastStateWithCourse = null;

		// remember the current bounded course
		CourseRange currentLegCourse = null;

		// keep track of the states in this leg - so we can apply the common course
		// bounds to them
		ArrayList<BoundedState> statesInThisLeg = new ArrayList<BoundedState>();

		// get the vehicle type
		VehicleType vType = space.getVehicleType();

		// PHASE 1 - Produce course bounds when none are present

		// ok, loop through the states, setting course limits for any unbounded
		// courses
		for (BoundedState currentState : space.states())
		{
			String thisLeg = currentState.getMemberOf();

			// ok, is this in a leg?
			if (thisLeg == null)
			{
				// ok, have we just finished a leg?
				if (!statesInThisLeg.isEmpty())
				{
					assignCommonState(currentLegCourse, statesInThisLeg);
					currentLegCourse = null;
				}

				// ok, we're not in a leg. relax it.
				lastStateWithCourse = applyRelaxedCourseBounds(lastStateWithCourse,
						currentState, vType);
			}
			else
			{
				// ok, we're in a leg - see if this is the first point (in which case we
				// allow relaxation),
				// or a successive point (in which case we just constrain)

				// is this the first item in this leg
				if (statesInThisLeg.isEmpty())
				{
					// yes. this is the first leg. We do need to allow some relaxation
					// on course from the previous one
					lastStateWithCourse = applyRelaxedCourseBounds(lastStateWithCourse,
							currentState, vType);

					// now we can probably store this state
					if (lastStateWithCourse != null)
						currentLegCourse = lastStateWithCourse.getCourse();
				}
				else
				{
					// ok, we won't be relaxing this leg - we're just going to further
					// constrain the course range

					// does this have a course consraint?
					CourseRange thisC = currentState.getCourse();
					if (thisC != null)
					{
						if (currentLegCourse == null)
						{
							// ok, store it
							currentLegCourse = new CourseRange(thisC);
						}
						else
						{
							// ok, constrain it.
							currentLegCourse.constrainTo(thisC);
						}
					}
				}

				// ok, remmber this leg
				statesInThisLeg.add(currentState);
			}
		}

		// ok, do we have a dangling set of states to be assigned?
		if (!statesInThisLeg.isEmpty())
		{
			assignCommonState(currentLegCourse, statesInThisLeg);
			currentLegCourse = null;
		}
	}

	/**
	 * apply this course constraint to all to all of the states in the list
	 * 
	 * @param commonCourse
	 * @param statesInThisLeg
	 * @throws IncompatibleStateException
	 */
	private void assignCommonState(CourseRange commonCourse,
			ArrayList<BoundedState> statesInThisLeg)
			throws IncompatibleStateException
	{
		// ok - apply the common bounded course to all the states in the leg

		// have we produced a constrained course?
		if (commonCourse != null)
		{
			// yes. we need to apply the min constraints to all the points in
			// that leg
			Iterator<BoundedState> iter = statesInThisLeg.iterator();
			while (iter.hasNext())
			{
				BoundedState boundedState = (BoundedState) iter.next();
				boundedState.constrainTo(commonCourse);
			}
		}

		// now we need to clear the remembered states, so we're ready for the
		// next leg
		statesInThisLeg.clear();
	}

	public BoundedState applyRelaxedCourseBounds(
			BoundedState lastStateWithCourse, BoundedState currentState,
			VehicleType vehicleType) throws IncompatibleStateException
	{
		// do we have vehicle reference data?
		if (vehicleType != null)
		{
			double maxRate = vehicleType.getMaxTurnRate();

			// do we have a previous state?
			if (lastStateWithCourse != null)
			{
				// ok, how long since that last observation?
				long millis = currentState.getTime().getTime()
						- lastStateWithCourse.getTime().getTime();

				// how many rads?
				double maxTurn = maxRate * millis / 1000.0d;

				// ok, we need to produce a new course constraint
				CourseRange lastKnown = lastStateWithCourse.getCourse();

				double newMin, newMax;

				// there's a challenging problem with expanding course data:
				// 1. if the range is 5 to 100, and maxTurn is 15, then the new
				// range is -10 to 111. fine.
				// 2. but, if the range is 5 to 355 and maxTurn is 14 then the new
				// range would be -10 to +10, which doesn't make sense.
				// 3. we need a fancy bit of logic/clipping to resolve this.
				// I suspect that it's something like if the outer range delta plus
				// two times maxTurn is greater than 360, then the acceptable range
				// is 0 to 360.
				double turnDelta = lastKnown.getMax() - lastKnown.getMin();

				// special case. if the delta is -ve, put it into the +ve domain. We
				// need to do this
				// to handle the case when we have a very large pie slice (over<commit_msg>fix bug in how we remember the last valid state<commit_after>package com.planetmayo.debrief.satc.model.contributions;

import java.util.ArrayList;
import java.util.Iterator;

import com.planetmayo.debrief.satc.model.VehicleType;
import com.planetmayo.debrief.satc.model.states.BaseRange.IncompatibleStateException;
import com.planetmayo.debrief.satc.model.states.BoundedState;
import com.planetmayo.debrief.satc.model.states.CourseRange;
import com.planetmayo.debrief.satc.model.states.ProblemSpace;

public class CourseAnalysisContribution extends BaseContribution
{
	private static final long serialVersionUID = 1L;

	public CourseAnalysisContribution()
	{
		super();
		setName("Course Analysis");
	}

	@Override
	public void actUpon(final ProblemSpace space) throws IncompatibleStateException
	{
		// remember the previous state
		BoundedState lastStateWithCourse = null;

		// remember the current bounded course
		CourseRange currentLegCourse = null;

		// keep track of the states in this leg - so we can apply the common course
		// bounds to them
		final ArrayList<BoundedState> statesInThisLeg = new ArrayList<BoundedState>();

		// get the vehicle type
		final VehicleType vType = space.getVehicleType();

		// ok, loop through the states, setting course limits for any unbounded
		// courses
		for (BoundedState currentState : space.states())
		{
			String thisLeg = currentState.getMemberOf();

			// ok, is this in a leg?
			if (thisLeg == null)
			{
				// ok, have we just finished a leg?
				if (!statesInThisLeg.isEmpty())
				{
					assignCommonState(currentLegCourse, statesInThisLeg);
					currentLegCourse = null;
				}

				// ok, we're not in a leg. relax it.
				lastStateWithCourse = applyRelaxedCourseBounds(lastStateWithCourse,
						currentState, vType);
			}
			else
			{
				// ok, we're in a leg - see if this is the first point (in which case we
				// allow relaxation),
				// or a successive point (in which case we just constrain)

				// is this the first item in this leg
				if (statesInThisLeg.isEmpty())
				{
					// yes. this is the first leg. We do need to allow some relaxation
					// on course from the previous one
					lastStateWithCourse = applyRelaxedCourseBounds(lastStateWithCourse,
							currentState, vType);

					// now we can probably store this state
					if (lastStateWithCourse != null)
						currentLegCourse = lastStateWithCourse.getCourse();
				}
				else
				{
					// ok, we won't be relaxing this leg - we're just going to further
					// constrain the course range

					// does this have a course consraint?
					CourseRange thisC = currentState.getCourse();
					if (thisC != null)
					{
						if (currentLegCourse == null)
						{
							// ok, store it
							currentLegCourse = new CourseRange(thisC);
						}
						else
						{
							// ok, constrain it.
							currentLegCourse.constrainTo(thisC);
						}
					}
					
					// but, we do need to know the DTG and course of the last 
					// state that had both.  If we've ever had a course constraint,
					// then we know we're able to use this state for the DTG.
					if(lastStateWithCourse != null)
						lastStateWithCourse = currentState;
				}

				// ok, remmber this leg
				statesInThisLeg.add(currentState);
			}
		}

		// ok, do we have a dangling set of states to be assigned?
		if (!statesInThisLeg.isEmpty())
		{
			assignCommonState(currentLegCourse, statesInThisLeg);
			currentLegCourse = null;
		}
	}

	/**
	 * apply this course constraint to all to all of the states in the list
	 * 
	 * @param commonCourse
	 * @param statesInThisLeg
	 * @throws IncompatibleStateException
	 */
	private void assignCommonState(CourseRange commonCourse,
			ArrayList<BoundedState> statesInThisLeg)
			throws IncompatibleStateException
	{
		// ok - apply the common bounded course to all the states in the leg

		// have we produced a constrained course?
		if (commonCourse != null)
		{
			// yes. we need to apply the min constraints to all the points in
			// that leg
			Iterator<BoundedState> iter = statesInThisLeg.iterator();
			while (iter.hasNext())
			{
				BoundedState boundedState = (BoundedState) iter.next();
				boundedState.constrainTo(commonCourse);
			}
		}

		// now we need to clear the remembered states, so we're ready for the
		// next leg
		statesInThisLeg.clear();
	}

	public BoundedState applyRelaxedCourseBounds(
			BoundedState lastStateWithCourse, BoundedState currentState,
			VehicleType vehicleType) throws IncompatibleStateException
	{
		// do we have vehicle reference data?
		if (vehicleType != null)
		{
			double maxRate = vehicleType.getMaxTurnRate();

			// do we have a previous state?
			if (lastStateWithCourse != null)
			{
				// ok, how long since that last observation?
				long millis = currentState.getTime().getTime()
						- lastStateWithCourse.getTime().getTime();
				
				// how many rads?
				double maxTurn = maxRate * millis / 1000.0d;

				// ok, we need to produce a new course constraint
				CourseRange lastKnown = lastStateWithCourse.getCourse();

				double newMin, newMax;

				// there's a challenging problem with expanding course data:
				// 1. if the range is 5 to 100, and maxTurn is 15, then the new
				// range is -10 to 111. fine.
				// 2. but, if the range is 5 to 355 and maxTurn is 14 then the new
				// range would be -10 to +10, which doesn't make sense.
				// 3. we need a fancy bit of logic/clipping to resolve this.
				// I suspect that it's something like if the outer range delta plus
				// two times maxTurn is greater than 360, then the acceptable range
				// is 0 to 360.
				double turnDelta = lastKnown.getMax() - lastKnown.getMin();

				// special case. if the delta is -ve, put it into the +ve domain. We
				// need to do this
				// to handle the case when we have a very large pie slice (over
{'fraction_non_alphanumeric': 0.08487993666989181, 'fraction_numerical': 0.006596886269680711, 'mean_word_length': 2.9575356769926904, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, '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': '4446531', 'n_tokens_mistral': 4189, 'n_tokens_neox': 3241, 'n_words': 1182}