//#define CIL_EMIT
//#define FCTC_STATS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Threading.Tasks;

using miew.Debugging;
using miew.Enumerable;
using miew.String;


namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial struct FeatureInfo
	{
		public FeatureInfo(String feature, int i_feat, Type maximal_type)
		{
			this.i_feat = i_feat;
			this.feature = feature;
			this.maximal_type = maximal_type;
			this.c_failures = 0;
		}

		public int i_feat;
		public String feature;
		public Type maximal_type;
		public long c_failures;

#if DEBUG
		public static explicit operator FeatureInfo(int i_feat)
		{
			return Grammar._singleton.tm.feat_arr[i_feat];
		}
#endif
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class FeatureConfig : IList<int>
	{
		public readonly static FeatureConfig Empty = new FeatureConfig(null, null, 0, new int[0]);

		public class Manager : ICollection<FeatureConfig>
		{
			readonly TypeMgr tm;
			int c_feat_max = 0;

			public Manager(TypeMgr tm)
			{
				this.tm = tm;
				pod.Add(String.Empty, Empty);
			}

			public Dictionary<String, FeatureConfig> pod = new Dictionary<String, FeatureConfig>();

			public FeatureConfig Get(Type for_example, IEnumerable<int> rgfix)
			{
				if (!rgfix.Any())
					return FeatureConfig.Empty;

				int[] rg = rgfix.Select(ix => new { tm.feat_arr[ix].maximal_type.m_level, ix })
								.OrderBy(a => a.m_level)
								.ThenBy(a => a.ix)
								.Select(a => a.ix)
								.ToArray();
				String s = rg.Select(ix => ix.ToString()).StringJoin("-");

				if (rg.Length > c_feat_max)
					c_feat_max = rg.Length;

				FeatureConfig fc;
				if (!pod.TryGetValue(s, out fc))
					pod.Add(s, fc = new FeatureConfig(tm, for_example, pod.Count, rg));
				return fc;
			}

			public int MaxFeaturesPerType { get { return c_feat_max; } }

			public void RetuneAll()
			{
				Task.Factory.StartNew(() =>
				{
#if false
					/// experiment with putting (only) the most failed path (i.e. SYNSEM) _last_, under the idea that it
					/// is already handled by quick-check.
					int ifeat_max_fail = -1;
					if (tm.g.qcs != null)
						ifeat_max_fail = tm.feat_arr.ArgMax(fi => fi.c_failures).i_feat;
#endif

					foreach (FeatureConfig fc in pod.Values)
					{
						if (fc.rg_fix.Length > 1)
						{
							/// note: depends on atomic publishing
							fc.rg_fix_autotune = fc.rg_fix
													.OrderByDescending(fix =>
														//fix == ifeat_max_fail ? -1 : 
														tm.feat_arr[fix].c_failures)
													.ToArray();
						}
					}
				});
			}
			public bool Contains(FeatureConfig item) { return pod.ContainsValue(item); }
			public void CopyTo(FeatureConfig[] array, int arrayIndex) { pod.Values.CopyTo(array, arrayIndex); }
			public int Count { get { return pod.Count; } }
			public bool IsReadOnly { get { return true; } }
			public bool Remove(FeatureConfig item) { throw new InvalidOperationException(); }
			public void Add(FeatureConfig item) { throw new InvalidOperationException(); }
			public void Clear() { throw new InvalidOperationException(); }
			public IEnumerator<FeatureConfig> GetEnumerator() { return pod.Values.AsEnumerable<FeatureConfig>().GetEnumerator(); }
			IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
		};

		unsafe FeatureConfig(TypeMgr tm, Type for_example, int ix, int[] rgfix)
		{
			this.tm = tm;
			this.rg_fix = this.rg_fix_autotune = rgfix;
			this.fc_ix = ix;

			//this._u_destr = rg_fix.Length == 0 ? (del_u_destr)_u_destr_internal_empty : _first_time;

			if (rg_fix.Length == 0)
				return;
			int max = int.MinValue;
			int min = int.MaxValue;
			foreach (int fix in rgfix)
			{
				if (fix < min)
					min = fix;
				if (fix > max)
					max = fix;
			}
			int range = max - min + 1;

			modulus = rg_fix.Length;
			if (modulus > 1)
			{
				int gen = 0;
				int* test_arr = stackalloc int[range];
				while (modulus <= range)
				{
					gen++;
					foreach (int fix in rg_fix)
					{
						int slot = fix % modulus;
						if (test_arr[slot] == gen)
							goto collision;
						test_arr[slot] = gen;
					}
					goto mod_ok;
				collision:
					modulus++;
				}
				throw new Exception();
			}
		mod_ok:
			this.fstfs = new FeatureSlotTfs(this, for_example);
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly TypeMgr tm;
		public TypeMgr TypeMgr { get { return tm; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly int fc_ix;
		public int ConfigIndex { get { return fc_ix; } }

		readonly public int[] rg_fix;
		public int[] rg_fix_autotune;
		readonly public int modulus;
		readonly public FeatureSlotTfs fstfs;

		public override String ToString()
		{
			return String.Format("#{0} {1}", fc_ix, rg_fix.Select(i => tm.feat_arr[i].feature.ToUpper()).StringJoin(" "));
		}

		public int this[int index]
		{
			get { return rg_fix[index]; }
			set { throw new InvalidOperationException(); }
		}

		public int IndexOf(int item)
		{
			for (int j = 0; j < rg_fix.Length; j++)
				if (rg_fix[j] == item)
					return j;
			return -1;
		}

		public bool Contains(int item)
		{
			for (int j = 0; j < rg_fix.Length; j++)
				if (rg_fix[j] == item)
					return true;
			return false;
		}

		public void CopyTo(int[] array, int arrayIndex)
		{
			for (int j = 0; j < rg_fix.Length; j++)
				array[arrayIndex++] = rg_fix[j];
		}

		public int Count { get { return rg_fix.Length; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsReadOnly { get { return true; } }

		public void Insert(int index, int item) { throw new InvalidOperationException(); }
		public void Add(int item) { throw new InvalidOperationException(); }
		public void Clear() { throw new InvalidOperationException(); }
		public bool Remove(int item) { throw new InvalidOperationException(); }
		public void RemoveAt(int index) { throw new InvalidOperationException(); }

#if false
		public delegate bool del_u_destr(Unification u, int m1, TfsEdge te2, Func<ConstraintPool, int, Edge, TfsEdge, bool> fn);

		public del_u_destr _u_destr;

		bool _first_time(Unification u, int m1, TfsEdge te2, Func<ConstraintPool, int, Edge, TfsEdge, bool> fn)
		{
			_u_destr =
				emit_DestructiveUnify( //u.TargetTray);
				u, m1, te2, fn);
			return _u_destr(u, m1, te2, fn);
		}


		del_u_destr emit_DestructiveUnify(//Tray tr)
			Unification u, int m1, TfsEdge te2, Func<ConstraintPool, int, Edge, TfsEdge, bool> fn)
		{
#if CIL_EMIT
			Tray tr = u.TargetTray;

			System.Type[] t_args =
			{
				typeof(FeatureConfig),									// arg.0	'this'
				typeof(Unification),									// arg.1	'u'
				typeof(int),											// arg.2	'm1'
				typeof(TfsEdge),										// arg.3	'te2'
				typeof(Func<ConstraintPool, int, Edge, TfsEdge, bool>)	// arg.4	'fn'
			};
			DynamicMethod dm = new DynamicMethod(String.Empty, typeof(bool), t_args, typeof(FeatureConfig), true);
			ILGenerator il = dm.GetILGenerator();

			il.DeclareLocal(typeof(Tray));				// loc.0	'tr'
			il.DeclareLocal(typeof(int));				// loc.1	'm2'
			il.DeclareLocal(typeof(ConstraintPool[]));	// loc.2	'rgcp'
			il.DeclareLocal(typeof(ConstraintPool));	// loc.3	'cp'
			il.DeclareLocal(typeof(Edge));				// loc.4	'ne1'
			il.DeclareLocal(typeof(Edge));				// loc.5	'ge'

			/// tr = u.TargetTray
			/// rgcp = tr.Pools;
			il.Emit(OpCodes.Ldarg_1);
			il.Emit(OpCodes.Call, typeof(Unification).GetMethod("get_TargetTray"));
			il.Emit(OpCodes.Dup);
			il.Emit(OpCodes.Stloc_0);
			il.Emit(OpCodes.Ldfld, typeof(Tray).GetField("Pools"));
			il.Emit(OpCodes.Stloc_2);

			/// m2 = te2.Edge.Mark
			il.Emit(OpCodes.Ldarga_S, 3);
			il.Emit(OpCodes.Ldfld, typeof(TfsEdge).GetField("Edge"));
			il.Emit(OpCodes.Ldfld, typeof(Edge).GetField("Mark"));
			il.Emit(OpCodes.Stloc_1);

			foreach (int fix in rg_fix)
			{
				ConstraintPool cp = tr.Pools[fix];
				Label l_next_iter = il.DefineLabel();

				/// cp = rgcp[@fix]
				il.Emit(OpCodes.Ldloc_2);
				il.Emit(OpCodes.Ldc_I4, fix);
				il.Emit(OpCodes.Ldelem_Ref);
				il.Emit(OpCodes.Stloc_3);

				/// danger: overwriting te2.Edge now; original mark is preserved in m2
				/// if (cp.TryGetEdge(m2, &te2.Edge))
				cp.emit_TryGetEdge(il,
					g => il.Emit(OpCodes.Ldloc_3),
					g => il.Emit(OpCodes.Ldloc_1),
					g =>
					{
						il.Emit(OpCodes.Ldarga_S, 3);
						il.Emit(OpCodes.Ldflda, typeof(TfsEdge).GetField("Edge"));
					});
				il.Emit(OpCodes.Brfalse, l_next_iter);

				/// cp.TryGetEdge(m1, out ne1);
				cp.emit_TryGetEdge(il,
					g => il.Emit(OpCodes.Ldloc_3),
					g => il.Emit(OpCodes.Ldarg_2),
					g =>
					{
						il.Emit(OpCodes.Ldloca_S, 4);
						il.Emit(OpCodes.Ldflda, typeof(TfsEdge).GetField("Edge"));
					});
				il.Emit(OpCodes.Pop);


				il.MarkLabel(l_next_iter);
			}
			/// return true
			il.Emit(OpCodes.Ldc_I4_1);
			il.Emit(OpCodes.Ret);

			del_u_destr test = (del_u_destr)dm.CreateDelegate(typeof(del_u_destr), this);

			bool xt = test(u, m1, te2, fn);
			Console.WriteLine(xt);

#endif
			//return _emit_u_destr_closure(u, m1, te2, fn);
			return _u_destr_internal;
			//return (del_u_destr)dm.CreateDelegate(typeof(del_u_destr), this);
		}

		del_u_destr _emit_u_destr_closure(Unification _u, int _m1, TfsEdge _te2, Func<ConstraintPool, int, Edge, TfsEdge, bool> _fn)
		{
#if false
			//Console.WriteLine("{0} {1}", cfg_ix, rg_fix.Select(ix => ix.ToString()).StringJoin("-"));
			Tray tr = _u.TargetTray;
			ConstraintPool[] rgcp = rg_fix.Select(i => tr.Pools[i]).ToArray();

			return new del_u_destr((u, m1, te2, fn) =>
			{
				int m2 = te2.Edge.Mark;
				ConstraintPool cp;
				Edge ne1;
				Edge ge;

				//////////////////////////////////////////////////

				cp = rgcp[0];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 1)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[1];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 2)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[2];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 3)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[3];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 4)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[4];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 5)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[5];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 6)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[6];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 7)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[7];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 8)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[8];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 9)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[9];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 10)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[10];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 11)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[11];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 12)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[12];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				if (rg_fix.Length == 13)
					return true;

				//////////////////////////////////////////////////

				cp = rgcp[13];
				if (cp.TryGetEdge(m2, out te2.Edge))
				{
					cp.TryGetEdge(m1, out ne1);
					if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
					{
						if (!fn(cp, m1, ne1, te2))
						{
							foreach (ConstraintPool _cp in rgcp)
								if (_cp.TryRemoveEdge(m1, out ge))
									tr.RudeTruncate(ge);
							return false;
						}
					}
					if (m1 != m2 && te2.IsTarget)
						u._prune_below(cp, m2);
				}

				return true;
			});
#endif
			return null;
		}


		bool _u_destr_internal_empty(Unification u, int m1, TfsEdge te2, Func<ConstraintPool, int, Edge, TfsEdge, bool> fn)
		{
			return true;
		}

#if true
		bool _u_destr_internal(Unification u, int m1, TfsEdge te2, Func<ConstraintPool, int, Edge, TfsEdge, bool> fn)
		{
#if false
			Tray tr = u.TargetTray;
			int m2 = te2.Edge.Mark;
			ConstraintPool[] rgcp = tr.Pools;
			ConstraintPool cp;
			Edge ne1;
			Edge ge;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[0]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 1)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[1]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 2)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[2]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 3)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[3]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 4)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[4]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 5)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[5]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 6)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[6]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 7)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[7]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 8)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[8]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 9)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[9]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 10)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[10]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 11)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[11]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 12)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[12]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}

			if (rg_fix.Length == 13)
				return true;

			//////////////////////////////////////////////////

			cp = rgcp[rg_fix[13]];
			if (cp.TryGetEdge(m2, out te2.Edge))
			{
				cp.TryGetEdge(m1, out ne1);
				if (ne1.Mark != te2.Edge.Mark || ne1.FlagsId != te2.Edge.FlagsId)
				{
					if (!fn(cp, m1, ne1, te2))
					{
						foreach (int j in rg_fix)
							if (rgcp[j].TryRemoveEdge(m1, out ge))
								tr.RudeTruncate(ge);
						return false;
					}
				}
				if (m1 != m2 && te2.IsTarget)
					u._prune_below(cp, m2);
			}
#endif
			return true;
		}
#endif
#endif
		_int_enumerable ie = null;

		public IEnumerator<int> GetEnumerator()
		{
			ie = ie ?? new _int_enumerable(this);
			return ie.GetEnumerator();
		}

		IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			ie = ie ?? new _int_enumerable(this);
			return ie.GetEnumerator();
		}


		class _int_enumerable : IEnumerable<int>
		{
			Func<int>[] rgfn;

			static Func<int>[] lfn = null;

			public _int_enumerable(FeatureConfig fc)
			{
				if (lfn == null)
					Interlocked.CompareExchange(ref lfn, new Func<int>[fc.tm.Features.Count], null);
				this.rgfn = fc.rg_fix.Select(i =>
				{
					if (i >= lfn.Length || lfn[i] == null)
					{
						DynamicMethod dm = new DynamicMethod(String.Empty, typeof(int), null, typeof(_int_enumerable), true);
						ILGenerator il = dm.GetILGenerator(256);
						il.Emit(OpCodes.Ldc_I4, i);
						il.Emit(OpCodes.Ret);
						lfn[i] = (Func<int>)dm.CreateDelegate(typeof(Func<int>));
					}
					return lfn[i];
				}).ToArray();
			}

			public IEnumerator<int> GetEnumerator() { return new _enum(rgfn); }

			IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new _enum(rgfn); }

			class _enum : IEnumerator<int>
			{
				uint ix = 0xFFFFFFFF;
				Func<int> cur = null;
				Func<int>[] rgfn;

				public _enum(Func<int>[] rgfn) { this.rgfn = rgfn; }

				public int Current { get { return cur(); } }

				object IEnumerator.Current { get { return cur(); } }

				public bool MoveNext()
				{
					if (++ix >= rgfn.Length)
						return false;
					cur = rgfn[ix];
					return true;
				}

				public void Reset()
				{
					ix = 0xFFFFFFFF;
					cur = null;
				}

				public void Dispose() { }
			};
		};
	};
}