<commit_before>﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using ReClassNET.Nodes;

namespace ReClassNET.Util
{
	public class ClassManager
	{
		public delegate void ClassesChangedEvent(ClassNode sender);
		public static event ClassesChangedEvent ClassAdded;
		public static event ClassesChangedEvent ClassRemoved;

		private static readonly List<ClassNode> classes = new List<ClassNode>();

		public static IEnumerable<ClassNode> Classes => classes;

		public static ClassNode CreateClass()
		{
			var node = new ClassNode();
			AddClass(node);
			return node;
		}

		internal static void AddClass(ClassNode node)
		{
			Contract.Requires(node != null);

			if (!classes.Contains(node))
			{
				classes.Add(node);

				ClassAdded?.Invoke(node);
			}
		}

		public static void Clear()
		{
			var temp = classes.ToList();

			classes.Clear();

			foreach (var node in temp)
			{
				ClassRemoved?.Invoke(node);
			}
		}

		private static IEnumerable<ClassNode> GetClassReferences(ClassNode node)
		{
			Contract.Requires(node != null);

			return classes.Where(c => c != node).Where(c => c.Descendants().Where(n => (n as BaseReferenceNode)?.InnerNode == node).Any());
		}

		public static void Remove(ClassNode node)
		{
			Contract.Requires(node != null);

			var references = GetClassReferences(node).ToList();
			if (references.Any())
			{
				throw new ClassReferencedException(references);
			}

			if (classes.Remove(node))
			{
				ClassRemoved?.Invoke(node);
			}
		}

		public static void RemoveUnusedClasses()
		{
			var toRemove = classes
				.Except(classes.Where(x => GetClassReferences(x).Any())) // check for references
				.Where(c => c.Nodes.All(n => n is BaseHexNode)) // check if only hex nodes are present
				.ToList();
			foreach (var node in toRemove)
			{
				if (classes.Remove(node))
				{
					ClassRemoved?.Invoke(node);
				}
			}
		}

		public static bool IsCycleFree(ClassNode parent, ClassNode check)
		{
			Contract.Requires(parent != null);
			Contract.Requires(check != null);

			return IsCycleFree(parent, check, classes);
		}

		public static bool IsCycleFree(ClassNode parent, ClassNode check, IEnumerable<ClassNode> classes)
		{
			Contract.Requires(parent != null);
			Contract.Requires(check != null);
			Contract.Requires(classes != null);
			Contract.Requires(Contract.ForAll(classes, c => c != null));

			var toCheck = new HashSet<ClassNode>(
				parent.Yield()
				.Traverse(
					c => c.Nodes
					.Where(n => n is ClassInstanceNode || n is ClassInstanceArrayNode)
					.Select(n => ((BaseReferenceNode)n).InnerNode as ClassNode)
				)
				.Where(c => c != parent)
			);

			if (!toCheck.Add(check))
			{
				return false;
			}

			if (!IsCycleFreeUp(parent, toCheck, classes))
			{
				return false;
			}

			return true;
		}

		private static bool IsCycleFreeUp(ClassNode root, HashSet<ClassNode> seen, IEnumerable<ClassNode> classes)
		{
			Contract.Requires(root != null);
			Contract.Requires(seen != null);
			Contract.Requires(Contract.ForAll(seen, c => c != null));
			Contract.Requires(Contract.ForAll(classes, c => c != null));

			if (!seen.Add(root))
			{
				return false;
			}

			foreach (var cls in classes/*.Except(seen)*/)<commit_msg>Fixed cycle detection.<commit_after>﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using ReClassNET.Nodes;

namespace ReClassNET.Util
{
	public class ClassManager
	{
		public delegate void ClassesChangedEvent(ClassNode sender);
		public static event ClassesChangedEvent ClassAdded;
		public static event ClassesChangedEvent ClassRemoved;

		private static readonly List<ClassNode> classes = new List<ClassNode>();

		public static IEnumerable<ClassNode> Classes => classes;

		public static ClassNode CreateClass()
		{
			var node = new ClassNode();
			AddClass(node);
			return node;
		}

		internal static void AddClass(ClassNode node)
		{
			Contract.Requires(node != null);

			if (!classes.Contains(node))
			{
				classes.Add(node);

				ClassAdded?.Invoke(node);
			}
		}

		public static void Clear()
		{
			var temp = classes.ToList();

			classes.Clear();

			foreach (var node in temp)
			{
				ClassRemoved?.Invoke(node);
			}
		}

		private static IEnumerable<ClassNode> GetClassReferences(ClassNode node)
		{
			Contract.Requires(node != null);

			return classes.Where(c => c != node).Where(c => c.Descendants().Where(n => (n as BaseReferenceNode)?.InnerNode == node).Any());
		}

		public static void Remove(ClassNode node)
		{
			Contract.Requires(node != null);

			var references = GetClassReferences(node).ToList();
			if (references.Any())
			{
				throw new ClassReferencedException(references);
			}

			if (classes.Remove(node))
			{
				ClassRemoved?.Invoke(node);
			}
		}

		public static void RemoveUnusedClasses()
		{
			var toRemove = classes
				.Except(classes.Where(x => GetClassReferences(x).Any())) // check for references
				.Where(c => c.Nodes.All(n => n is BaseHexNode)) // check if only hex nodes are present
				.ToList();
			foreach (var node in toRemove)
			{
				if (classes.Remove(node))
				{
					ClassRemoved?.Invoke(node);
				}
			}
		}

		public static bool IsCycleFree(ClassNode parent, ClassNode check)
		{
			Contract.Requires(parent != null);
			Contract.Requires(check != null);

			return IsCycleFree(parent, check, classes);
		}

		public static bool IsCycleFree(ClassNode parent, ClassNode check, IEnumerable<ClassNode> classes)
		{
			Contract.Requires(parent != null);
			Contract.Requires(check != null);
			Contract.Requires(classes != null);
			Contract.Requires(Contract.ForAll(classes, c => c != null));

			var toCheck = new HashSet<ClassNode>(
				check.Yield()
				.Traverse(
					c => c.Nodes
					.Where(n => n is ClassInstanceNode || n is ClassInstanceArrayNode)
					.Select(n => ((BaseReferenceNode)n).InnerNode as ClassNode)
				)
			);

			if (!IsCycleFreeUp(parent, toCheck, classes))
			{
				return false;
			}

			return true;
		}

		private static bool IsCycleFreeUp(ClassNode root, HashSet<ClassNode> seen, IEnumerable<ClassNode> classes)
		{
			Contract.Requires(root != null);
			Contract.Requires(seen != null);
			Contract.Requires(Contract.ForAll(seen, c => c != null));
			Contract.Requires(Contract.ForAll(classes, c => c != null));

			if (!seen.Add(root))
			{
				return false;
			}

			foreach (var cls in classes/*.Except(seen)*/)
{'fraction_non_alphanumeric': 0.12593167701863353, 'fraction_numerical': 0.0, 'mean_word_length': 3.584341637010676, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 46, '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': '3351970', 'n_tokens_mistral': 2429, 'n_tokens_neox': 1971, 'n_words': 480}