/// <summary>
		///   Initializes a new instance.
		/// </summary>
		/// <param name="results">The result of the safety analysis the order analysis should be conducted for.</param>
		/// <param name="configuration">The model checker's configuration that determines certain model checker settings.</param>
		internal OrderAnalysis(SafetyAnalysisResults results, AnalysisConfiguration configuration)
		{
			Requires.NotNull(results, nameof(results));

			_results = results;
			_backend.InitializeModel(configuration, results.Model, results.Hazard);
		}
		/// <summary>
		///   Initializes a new instance.
		/// </summary>
		internal OrderAnalysisResults(SafetyAnalysisResults safetyAnalysisResults, TimeSpan time,
									  IDictionary<ISet<Fault>, IEnumerable<OrderRelationship>> orderRelationships)
		{
			SafetyAnalysisResults = safetyAnalysisResults;
			Time = time;
			OrderRelationships = orderRelationships;
		}
		/// <summary>
		///   Computes the order relationships for all minimal critical fault sets contained in the
		///   <paramref name="safetyAnalysisResults" />.
		/// </summary>
		/// <param name="safetyAnalysisResults">The results of the safety analysis the order relationships should be computed for.</param>
		/// <param name="configuration">The configuration settings of the model checker that should be used.</param>
		public static OrderAnalysisResults ComputeOrderRelationships(SafetyAnalysisResults safetyAnalysisResults,
																	 AnalysisConfiguration configuration)
		{
			var analysis = new OrderAnalysis(safetyAnalysisResults, configuration);
			return analysis.ComputeOrderRelationships();
		}
		/// <summary>
		///   Computes the order relationships for all minimal critical fault sets contained in the
		///   <paramref name="safetyAnalysisResults" />.
		/// </summary>
		/// <param name="safetyAnalysisResults">The results of the safety analysis the order relationships should be computed for.</param>
		public static OrderAnalysisResults ComputeOrderRelationships(SafetyAnalysisResults safetyAnalysisResults)
		{
			return ComputeOrderRelationships(safetyAnalysisResults, AnalysisConfiguration.Default);
		}
		/// <summary>
		///   Computes the minimal critical sets for the <paramref name="hazard" />.
		/// </summary>
		/// <param name="model">The model the safety analysis should be conducted for.</param>
		/// <param name="hazard">The hazard the minimal critical sets should be computed for.</param>
		/// <param name="maxCardinality">
		///   The maximum cardinality of the fault sets that should be checked. By default, all minimal
		///   critical fault sets are determined.
		/// </param>
		public SafetyAnalysisResults ComputeMinimalCriticalSets(ModelBase model, Formula hazard, int maxCardinality = Int32.MaxValue)
		{
			Requires.NotNull(model, nameof(model));
			Requires.NotNull(hazard, nameof(hazard));

			ConsoleHelpers.WriteLine("Running Deductive Cause Consequence Analysis.");

			var heuristicWatch = new Stopwatch();
			var stopwatch = new Stopwatch();
			stopwatch.Start();

			var allFaults = model.Faults;
			FaultSet.CheckFaultCount(allFaults.Length);

			var forcedFaults = allFaults.Where(fault => fault.Activation == Activation.Forced).ToArray();
			var suppressedFaults = allFaults.Where(fault => fault.Activation == Activation.Suppressed).ToArray();
			var nondeterministicFaults = allFaults.Where(fault => fault.Activation == Activation.Nondeterministic).ToArray();
			var nonSuppressedFaults = allFaults.Where(fault => fault.Activation != Activation.Suppressed).ToArray();

			ConsoleHelpers.WriteLine();
			ConsoleHelpers.WriteLine($"Of the {allFaults.Length} faults contained in the model,");
			ConsoleHelpers.WriteLine($"   {suppressedFaults.Length} faults are suppressed,");
			ConsoleHelpers.WriteLine($"   {forcedFaults.Length} faults are forced, and");
			ConsoleHelpers.WriteLine($"   {nondeterministicFaults.Length} faults are nondeterministically activated.");
			ConsoleHelpers.WriteLine();

			_suppressedSet = new FaultSet(suppressedFaults);
			_forcedSet = new FaultSet(forcedFaults);

			var isComplete = true;

			// Remove information from previous analyses
			Reset(model);

			// Initialize the backend, the model, and the analysis results
			switch (Backend)
			{
				case SafetyAnalysisBackend.FaultOptimizedOnTheFly:
					_backend = new FaultOptimizationBackend();
					break;
				case SafetyAnalysisBackend.FaultOptimizedStateGraph:
					_backend = new StateGraphBackend();
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}

			_backend.OutputWritten += output => OutputWritten?.Invoke(output);
			_backend.InitializeModel(Configuration, model, hazard);
			_results = new SafetyAnalysisResults(model, hazard, suppressedFaults, forcedFaults, Heuristics, FaultActivationBehavior);

			// Remember all safe sets of current cardinality - we need them to generate the next power set level
			var currentSafe = new HashSet<FaultSet>();

			// We check fault sets by increasing cardinality; this is, we check the empty set first, then
			// all singleton sets, then all sets with two elements, etc. We don't check sets that we
			// know are going to be critical sets due to monotonicity
			for (var cardinality = 0; cardinality <= nonSuppressedFaults.Length; ++cardinality)
			{
				// Generate the sets for the current level that we'll have to check
				var sets = GeneratePowerSetLevel(cardinality, nonSuppressedFaults, currentSafe);
				currentSafe.Clear();

				// Remove all sets that conflict with the forced or suppressed faults; these sets are considered to be safe.
				// If no sets remain, skip to the next level
				sets = RemoveInvalidSets(sets, currentSafe);
				if (sets.Count == 0)
					continue;

				// Abort if we've exceeded the maximum fault set cardinality; doing the check here allows us
				// to report the analysis as complete if the maximum cardinality is never reached
				if (cardinality > maxCardinality)
				{
					isComplete = false;
					break;
				}

				if (cardinality == 0)
					ConsoleHelpers.WriteLine("Checking the empty fault set...");
				else
					ConsoleHelpers.WriteLine($"Checking {sets.Count} sets of cardinality {cardinality}...");

				// use heuristics
				var setsToCheck = new LinkedList<FaultSet>(sets);
				foreach (var heuristic in Heuristics)
				{
					var count = setsToCheck.Count;

					heuristicWatch.Restart();
					heuristic.Augment((uint)cardinality, setsToCheck);

					count = setsToCheck.Count - count;
					if (count > 0)
						ConsoleHelpers.WriteLine($"    {heuristic.GetType().Name} made {count} suggestions in {heuristicWatch.Elapsed.TotalMilliseconds}ms.");
				}

				// We have to check each set - heuristics may add further during the loop
				while (setsToCheck.Count > 0)
				{
					var set = setsToCheck.First.Value;

					var isCurrentLevel = sets.Remove(set); // returns true if set was actually contained
					setsToCheck.RemoveFirst();

					// for current level, we already know the set is valid
					var isValid = isCurrentLevel || IsValid(set);

					// the set is invalid if it exceeds the maximum cardinality level
					isValid &= set.Cardinality <= maxCardinality;

					var isSafe = true;
					if (isValid)
						isSafe = CheckSet(set, allFaults, !isCurrentLevel);

					if (isSafe && isCurrentLevel)
						currentSafe.Add(set);

					// inform heuristics about result and give them the opportunity to add further sets
					foreach (var heuristic in Heuristics)
						heuristic.Update(setsToCheck, set, isSafe);
				}

				// in case heuristics removed a set (they shouldn't)
				foreach (var set in sets)
				{
					var isSafe = CheckSet(set, allFaults, false);
					if (isSafe)
						currentSafe.Add(set);
				}
			}

			// Reset the nondeterministic faults so as to not influence subsequent analyses
			foreach (var fault in nondeterministicFaults)
				fault.Activation = Activation.Nondeterministic;

			// due to heuristics usage, we may have informatiuon on non-minimal critical sets
			var minimalCritical = RemoveNonMinimalCriticalSets();

			_results.IsComplete = isComplete;
			_results.Time = stopwatch.Elapsed;
			_results.SetResult(minimalCritical, _checkedSets, _counterExamples, _exceptions);

			return _results;
		}