//#define CHECK_COUNTS
#define FEATURE_PATH
//#define COUNTING

using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

using miew.Debugging;
using miew.Enumerable;

namespace agree
{
	public unsafe partial class UnificationQd
	{
		ParseControl ctrl;
		TypeMgr tm;

		const int TopmostMark = 1;
		const int WellFormedSlots = 400;
		const int MaxParticipants = 16;

		ArrayTfs[] participants;
		int c_participants;

		Scratch* ps_base, next_base;
		Scratch** bases;

		bool f_args_delete;
		bool f_restrict;
		ushort*[] rgpfix;

		int c_slot_alloc, c_edges, next_mark, next_coref_mark, i_ate;

		arr_tfs_entry* _pate;
		ushort* _hidx, _rgn;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public UnificationQd(TypeMgr tm)
		{
#if DEBUG
			_singleton = this;
#if FEATURE_PATH
			if (Debugger.IsAttached)
				this.stk = new Stack<int>();
#endif
#endif
			this.tm = tm;
			this.participants = new ArrayTfs[MaxParticipants + 1];
			this.rgpfix = tm.rgpfix_allft;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public UnificationQd(ParseControl ctrl, bool f_args_delete, bool f_restrict)
			: this(ctrl.tm)
		{
			this.ctrl = ctrl;
			this.next_coref_mark = -1;
			this.next_mark = TopmostMark + 1;
			this.f_args_delete = f_args_delete;
			this.f_restrict = f_restrict;

			if (f_restrict && f_args_delete)
				this.rgpfix = tm.rgpfix_da_rs;
			else if (f_restrict)
				this.rgpfix = tm.rgpfix_restr;
			else if (f_args_delete)
				this.rgpfix = tm.rgpfix_dargs;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _init_node(Scratch* ps, int tfsix, Edge e)
		{
#if DEBUG
			Debug.Assert(ps->tfsix == 0 && tfsix != 0);
			//Debug.Assert(true_bases(tfsix) <= ps && ps < true_bases(tfsix + 1));
#endif
			ps->f = e.FlagsId & ~Edge.Flag.Coreference;
			ps->m_src = e.Mark;
			ps->tfsix = (short)tfsix;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Scratch* _fallback_fetch(ArrayTfs tfs, Scratch* ps, int i_feat)
		{
			Debug.Assert(ps->m_src != 0 && ps->forward >= 0);
			arr_tfs_entry* pe;
			int mix, mark, tfsix = ps->tfsix, fwd = ps->forward;
		fallback:
			if ((mix = tfs.hidx[(byte)(i_feat + (mark = ps->m_src))]) != 0)
				fixed (arr_tfs_entry* _pe = tfs.entries)
					do
						if ((pe = _pe + mix - 1)->i_feat == i_feat && pe->mark == mark)
						{
							if (pe->e_FlagsId == Edge.Flag.PrunedDuringParsing)
								break;
							if ((ps = bases[tfsix] + ((mark = pe->e_Mark) < 0 ? mark : mix))->tfsix == 0)
							{
								ps->f = pe->e_FlagsId & ~Edge.Flag.Coreference;
								ps->m_src = mark;
								ps->tfsix = (short)tfsix;
							}
							else
								while ((fwd = ps->forward) < 0)
									ps = ps_base - fwd;
							return ps;
						}
					while ((mix = tfs.rg_next[mix - 1]) != 0);
			if (fwd <= 0)
				return null;
			fwd = -(ps = ps_base + fwd)->forward;
			tfs = participants[tfsix = ps->tfsix];
			goto fallback;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool _duplex_node(Scratch* ps0, Scratch* ps1)
		{
			Edge.Flag f0, f1, nf;
			if ((f0 = ps0->f) == (f1 = ps1->f) || f1 == 0)
				nf = f0;
			else if (f0 == 0)
				nf = f1;
			else if ((nf = tm.UnifyTypesNoCoref(f0, f1)) == Edge.Flag.Bottom)
				return false;

			if ((nf & Edge.Flag.EtmNonBareType) == 0)
				_forward(ps1, ps0, nf, false);
			else
			{
				if (ps0->m_src != 0 && ps1->m_src != 0)
				{
					int missing0 = 0, missing1 = 0;
					ArrayTfs tfs0 = participants[ps0->tfsix];
					ArrayTfs tfs1 = participants[ps1->tfsix];
					ushort* pfix = rgpfix[(int)(nf & Edge.Flag.MultiIdMask)], pfix_end = *pfix++ + pfix;
					do
					{
						Scratch* nps0 = _fallback_fetch(tfs0, ps0, *pfix);
						Scratch* nps1 = _fallback_fetch(tfs1, ps1, *pfix);
#if DEBUG
						_debug_nps(ps0, ps1, nps0, nps1);
#endif
						if (nps0 == null)
						{
							if (nps1 != null)
								missing0++;
						}
						else if (nps1 == null)
							missing1++;
						else if (nps0 != nps1 && !_duplex_node(nps0, nps1))
							return false;
					}
					while (++pfix < pfix_end);
					if (missing0 <= missing1)
						_forward(ps1, ps0, nf, missing0 != 0);
					else
						_forward(ps0, ps1, nf, missing1 != 0);
				}
				else if (ps0->m_src != 0)
					_forward(ps1, ps0, 0, false);
				else
					_forward(ps0, ps1, 0, false);

				if (nf != f0 && nf != f1)
				{
					_dereference(ref ps0);
					return _duplex_node(ps0, _init_tfs_node(tm.type_arr[(int)(nf & Edge.Flag.MultiIdMask)].Expanded));
				}
			}
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool _duplex_node_count(Scratch* ps0, Scratch* ps1, int suppress)
		{
			Edge.Flag f0, f1, nf;
			if ((f0 = ps0->f) == (f1 = ps1->f) || f1 == 0)
				nf = f0;
			else if (f0 == 0)
				nf = f1;
			else if ((nf = tm.UnifyTypesNoCoref(f0, f1)) == Edge.Flag.Bottom)
				return false;

			if ((nf & Edge.Flag.EtmNonBareType) == 0)
			{
				_join_counts(ps1, ps0, suppress);
				_forward(ps1, ps0, nf, false);
			}
			else
			{
				if (ps0->m_src != 0 && ps1->m_src != 0)
				{
#if DEBUG
					Debug.Assert(ps0 != ps1);
					_check_args(nf, ps0, ps1);
#endif
					int missing0 = 0, missing1 = 0;
					int suppress0 = ps0->c_visits == 0 ? 0 : 1;
					int suppress1 = ps1->c_visits == 0 ? 0 : 1;
					ArrayTfs tfs0 = participants[ps0->tfsix];
					ArrayTfs tfs1 = participants[ps1->tfsix];

					ushort* pfix = rgpfix[(int)(nf & Edge.Flag.MultiIdMask)], pfix_end = *pfix++ + pfix;
					do
					{
#if DEBUG && FEATURE_PATH
						_change_stack_top(*pfix);
#endif
						Scratch* nps0 = _fallback_fetch(tfs0, ps0, *pfix);
						Scratch* nps1 = _fallback_fetch(tfs1, ps1, *pfix);
#if DEBUG
						_debug_nps(ps0, ps1, nps0, nps1);
#endif
						if (nps0 == nps1)
						{
							if (nps0 != null)
								_register_count(nps0, suppress0 + suppress1);
						}
						else if (nps0 == null)
						{
							missing0++;
							if (nps1->c_visits == 0 && (nps1->f & Edge.Flag.EtmNonBareType) != 0)
								_unpaired_body_count(nps1);
							_register_count(nps1, suppress1);
						}
						else if (nps1 == null)
						{
							missing1++;
							if (nps0->c_visits == 0 && (nps0->f & Edge.Flag.EtmNonBareType) != 0)
								_unpaired_body_count(nps0);
							_register_count(nps0, suppress0);
						}
						else if (!_duplex_node_count(nps0, nps1, suppress0 + suppress1))
							return false;
					}
					while (++pfix < pfix_end);
#if DEBUG && FEATURE_PATH
					if (stk != null) stk.Pop();
#endif
					if (missing0 <= missing1)
					{
						_join_counts(ps1, ps0, suppress);
						_forward(ps1, ps0, nf, missing0 != 0);
					}
					else
					{
						_join_counts(ps0, ps1, suppress);
						_forward(ps0, ps1, nf, missing1 != 0);
					}
				}
				else if (ps0->m_src != 0)
				{
					if (ps0->c_visits == 0)
						_unpaired_body_count(ps0);
					_join_counts(ps1, ps0, suppress);
					_forward(ps1, ps0, 0, false);
				}
				else
				{
					if (ps1->c_visits == 0)
						_unpaired_body_count(ps1);
					_join_counts(ps0, ps1, suppress);
					_forward(ps0, ps1, 0, false);
				}
				if (nf != f0 && nf != f1)
				{
					_dereference(ref ps0);
					return _duplex_node_count(ps0, _init_tfs_node(tm.type_arr[(int)(nf & Edge.Flag.MultiIdMask)].Expanded), 1);
				}
			}
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _unpaired_body_count(Scratch* ps)
		{
			int suppress = ps->c_visits != 0 ? 1 : 0;
			ArrayTfs tfs = participants[ps->tfsix];
			ushort* pfix = rgpfix[(int)(ps->f & Edge.Flag.MultiIdMask)], pfix_end = *pfix++ + pfix;
			do
			{
#if DEBUG && FEATURE_PATH
				if (stk != null) stk.Push(*pfix);
#endif
				Scratch* nps;
				if ((nps = _fallback_fetch(tfs, ps, *pfix)) != null)
				{
#if DEBUG
					_debug_nps_single(ps, nps);
#endif
					if (nps->c_visits == 0)
					{
						if ((nps->f & Edge.Flag.EtmNonBareType) != 0)
							_unpaired_body_count(nps);
					}
					else if (nps->c_visits < 0)
						nps->c_visits = 0;

					_register_count(nps, suppress);
				}
#if DEBUG && FEATURE_PATH
				if (stk != null) stk.Pop();
#endif
			}
			while (++pfix < pfix_end);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _forward(Scratch* ps_src, Scratch* ps_dst, Edge.Flag f_dst, bool f_cross)
		{
			Debug.Assert(ps_src != ps_dst && ps_src->forward >= 0 && ps_dst->forward >= 0);
			Debug.Assert(!f_cross || ps_src->m_src != 0);

			int src_fw, dst_fw;
			if ((dst_fw = ps_dst->forward) == 0)
				dst_fw = (int)(ps_dst - ps_base);
			if (f_cross)
			{
				if ((src_fw = ps_src->forward) == 0)
					src_fw = (int)(ps_src - ps_base);
				ps_dst->forward = (short)src_fw;						///  O O  ->  OO  ->  O
			}
			ps_src->forward = (short)-dst_fw;							///  O O  ->  __O
			if (f_dst != 0)
				ps_dst->f = f_dst;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _register_count(Scratch* ps, int suppress)
		{
			Debug.Assert(ps->c_visits >= 0);
			if (--suppress == 0)
				return;
			if (ps->f == 0)
			{
				int n, o;
				ps->c_visits = (short)(n = (o = ps->c_visits) - suppress);

				if (o > 1 && n <= 1)
					c_edges += suppress;
				else if (o <= 1 && n > 1)
					c_edges -= suppress - 1;
				else
					c_edges -= suppress;
			}
			else
			{
				ps->c_visits -= (short)suppress;
				c_edges -= suppress;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _join_counts(Scratch* ps_src, Scratch* ps_dst, int suppress)
		{
			int c_src;
			if ((c_src = ps_src->c_visits) != 0 && ps_src->f != 0)
				c_edges -= c_src;

			if (ps_src->f == 0)
				suppress--;
			_register_count(ps_dst, suppress - c_src);
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _dereference(ref Scratch* ps)
		{
			while (ps->forward < 0)
				ps = ps_base - ps->forward;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Scratch* _init_tfs_node(Tfs tfs)
		{
			TfsSection ts = tfs as TfsSection;
			int tfsix = _add_tfs(ts != null ? (ArrayTfs)ts.mother : (ArrayTfs)tfs);
			Scratch* ps = bases[tfsix];
			_init_node(ps, tfsix, tfs.Edge);
			return ps;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		int _add_tfs(ArrayTfs atfs)
		{
			Debug.Assert(c_participants <= MaxParticipants);
			int tfsix, offs, c;
			participants[tfsix = ++c_participants] = atfs;
			c = _slot_count(atfs, &offs);
			bases[tfsix] = next_base + offs;
			next_base += c;
#if DEBUG
			if ((int)(next_base - ps_base) >= c_slot_alloc)
				throw new Exception();
#endif
			return tfsix;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		static int _slot_count(Tfs tfs, int* offs_out)
		{
			TfsSection ts = tfs as TfsSection;
			if (ts != null)
				tfs = ts.mother;
			ArrayTfs atfs = (ArrayTfs)tfs;
			int offs = -(atfs.next_coref_mark + 1);
			if (offs_out != null)
				*offs_out = offs;
			return offs + 1 + atfs.ScratchAlloc;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public MotherDaughterTfs UnifySections(MotherDaughterTfs mother, int i_first_arg, Tfs[] candidates)
		{
			Debug.Assert(mother.Edge.Mark == TopmostMark);
			Debug.Assert((mother.Edge.FlagsId & Edge.Flag.EtmNonBareType) != 0);
			Debug.Assert(mother.RuleDaughters.Length == 2); // temp

			c_slot_alloc = 1 + _slot_count(mother, null) +
								_slot_count(candidates[0], null) +
								_slot_count(candidates[1], null) +
								WellFormedSlots;

			Scratch** _pp = stackalloc Scratch*[MaxParticipants];
			bases = _pp - 1;

			Scratch* _ps = stackalloc Scratch[c_slot_alloc];
			ps_base = _ps;
			next_base = ps_base + 1;								// don't use global slot zero
#if DEBUG
			for (int i = 0; i < c_slot_alloc; i++, ps_base++)
				ps_base->_this = ps_base;
			ps_base = _ps;
#endif

			c_edges = -1;
			Scratch* ps_m = _init_tfs_node(mother);
			for (int i = 0; i < candidates.Length; i++)
			{
				TfsSection daughter = mother.RuleDaughters[i_first_arg++];
				Scratch* ps_d = ps_m + (daughter.Edge.Mark < 0 ? daughter.Edge.Mark : daughter.MotherSlotIndex);
				_init_node(ps_d, ps_m->tfsix, daughter.Edge);
				Scratch* ps_c = _init_tfs_node(candidates[i]);

				if (f_args_delete)
				{
					if (!_duplex_node(ps_d, ps_c))
						return null;
				}
				else
				{
					if (!_duplex_node_count(ps_d, ps_c, 0))
						return null;
				}

				/// adjust counts for daughter repetition
				_dereference(ref ps_d);
				if (f_args_delete)
				{
					if (ps_d->c_visits < 0)
						c_edges -= ps_d->c_visits;
				}
				else
				{
					Debug.Assert(ps_d->c_visits > 0);
					ps_d->c_visits--;
				}
			}

			/// mother completion
			_unpaired_body_count(ps_m);
			_register_count(ps_m, 0);

			return _write_tfs(ps_m);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public MotherDaughterTfs UnifySection(TfsSection daughter, Tfs candidate)
		{
			MotherDaughterTfs mother = (MotherDaughterTfs)daughter.mother;
			Debug.Assert(mother.Edge.Mark == TopmostMark);
			Debug.Assert(daughter.Edge.Mark != TopmostMark);
			Debug.Assert((mother.Edge.FlagsId & Edge.Flag.EtmNonBareType) != 0);

			c_slot_alloc = 1 + _slot_count(mother, null) + _slot_count(candidate, null) + WellFormedSlots;

			Scratch** _pp = stackalloc Scratch*[MaxParticipants];
			bases = _pp - 1;

			Scratch* _ps = stackalloc Scratch[c_slot_alloc];
			ps_base = _ps;
			next_base = ps_base + 1;								// don't use global slot zero
#if DEBUG
			for (int i = 0; i < c_slot_alloc; i++, ps_base++)
				ps_base->_this = ps_base;
			ps_base = _ps;
#endif

			/// initialization
			int dm = daughter.Edge.Mark;
			Scratch* ps_m = _init_tfs_node(mother);
			Scratch* ps_c = _init_tfs_node(candidate);
			Scratch* ps_d = ps_m + (dm < 0 ? dm : daughter.MotherSlotIndex);
			_init_node(ps_d, ps_m->tfsix, daughter.Edge);

			/// forwarding pass
			c_edges = -1;
			if (f_args_delete)
			{
				if (!_duplex_node(ps_d, ps_c))
					return null;
			}
			else
			{
				if (!_duplex_node_count(ps_d, ps_c, 0))
					return null;
			}

			/// mother completion
			_unpaired_body_count(ps_m);
			_register_count(ps_m, 0);

			/// adjust counts for daughter repetition
			_dereference(ref ps_d);
			if (f_args_delete)
			{
				if (ps_d->c_visits < 0)
					c_edges -= ps_d->c_visits;
			}
			else
			{
				Debug.Assert(ps_d->c_visits > 0);
				ps_d->c_visits--;
			}

			//Debug.Assert(c_edges >= i_ate, "Count failed", "c_edges: {0} actual: {1} Input: {2}", c_edges, i_ate, ctrl.ts_input.Source.Text);
			//if (c_edges < i_ate)
			//    Nop.X();
			//if (c_edges - i_ate > 1)
			//    Nop.X();
			////Debug.Assert(i_ate == c_edges);
			//if (i_ate != c_edges)
			//    Nop.X();
			//else
			//    Console.Write(".");

#if CHECK_COUNTS
			_check_counts(entries, ps_m);
#endif
			return _write_tfs(ps_m);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		MotherDaughterTfs _write_tfs(Scratch* ps_m)
		{
			arr_tfs_entry[] entries = new arr_tfs_entry[c_edges];
			ushort[] hidx = new ushort[256];
			ushort[] rgn = new ushort[c_edges];
			fixed (arr_tfs_entry* __pate = entries)
			fixed (ushort* __hidx = hidx)
			fixed (ushort* __rgn = rgn)
			{
				i_ate = 0;
				_pate = __pate;
				_hidx = __hidx;
				_rgn = __rgn;
				_write_node(ps_m, TopmostMark);
			}
			return new MotherDaughterTfs(ctrl,
							new Edge(ps_m->f, TopmostMark),
							entries,
							hidx,
							rgn,
							next_mark,
							next_coref_mark,
							f_restrict);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _write_node(Scratch* ps, int m_dst)
		{
			ArrayTfs tfs = participants[ps->tfsix];
			ushort* pfix = rgpfix[(int)(ps->f & Edge.Flag.MultiIdMask)], pfix_end = *pfix++ + pfix;
			do
			{
				Scratch* nps = _fallback_fetch(tfs, ps, *pfix);
				if (nps != null)
				{
					int m, c = nps->c_visits;
					Debug.Assert(c > 0 || (c < 0 && f_args_delete));
					if (c < 0)
						c = 1;
					if ((m = nps->m_copy) == 0)
					{
						bool non_bare = (nps->f & Edge.Flag.EtmNonBareType) != 0;
						if (c > 1)
						{
							m = nps->m_copy = (short)next_coref_mark--;
							nps->f |= Edge.Flag.Coreference;
						}
						else if (nps->f == 0)
							continue;
						else if (non_bare)
							m = nps->m_copy = (short)next_mark++;

						if (non_bare)
							_write_node(nps, m);
					}
					_pate->i_feat = *pfix;
					_pate->mark = m_dst;
					_pate->e_FlagsId = nps->f;
					_pate->e_Mark = m;
					_pate++;
					if (++i_ate > c_edges)
						throw new Exception("internal error counting edges. input: " + ctrl.ts_input.Source.Text);

					ushort* pus = _hidx + (byte)(*pfix + m_dst);
					*_rgn++ = *pus;
					*pus = (ushort)i_ate;
				}
			}
			while (++pfix < pfix_end);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool Check(Tfs tfs0, Tfs tfs1)
		{
			c_slot_alloc = 1 + _slot_count(tfs0, null) + _slot_count(tfs1, null) + WellFormedSlots;

			Scratch** _pp = stackalloc Scratch*[MaxParticipants];
			bases = _pp - 1;

			Scratch* _ps = stackalloc Scratch[c_slot_alloc];
			ps_base = _ps;
			next_base = ps_base + 1;								// don't use global slot zero
#if DEBUG
			for (int i = 0; i < c_slot_alloc; i++, ps_base++)
				ps_base->_this = ps_base;
			ps_base = _ps;
#endif
			return _duplex_node(_init_tfs_node(tfs0), _init_tfs_node(tfs1));
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		partial struct Scratch
		{
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public Edge.Flag f;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public int m_src;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public short forward;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public short tfsix;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public short c_visits;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public short m_copy;

#if DEBUG
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public Scratch* _this;
#endif
		};
	};
}