#define MINIFUNCS
//#define CHECK_TOPMOST_FEAT
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Collections;
using System.Runtime.InteropServices;
using miew.Debugging;
using miew.Enumerable;
using miew.ReadOnly;
namespace agree
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// N-way unification (full)
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if DEBUG
public unsafe partial class UnificationNWay
#else
public unsafe partial struct UnificationNWay
#endif
{
const int CorefsMax = 300;
const int ArgArityMax = 12;
const int MaxParticipants = 16; // (13)
const int TopmostMark = 1;
const int DeferredWrite = int.MinValue;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
TypeMgr tm;
ParseControl ctrl;
ushort*[] rgpfix;
Tfs[] participants;
#if NODE_INDEX
NodeIndex** pp_ni_ptx;
bool f_any_ni;
#endif
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
byte c_participants;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
NwayArgs* _corefs, pa_next;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
NwayArgs** ppmb_next, pp_map_base;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
NwayArgs*** participant_bases;
arr_tfs_entry** coref_links;
#if CHECK_TOPMOST_FEAT
ushort c_fix_topmost;
#endif
public int next_mark;
public int next_coref_mark;
public int min_coref_in_mark;
public arr_tfs_entry* p_ate;
public arr_tfs_entry* p_ate_base;
int c_edge_alloc;
int c_plug_remain;
PlugPair* plugs;
PlugPair* plugs_end;
byte* del_daughter_map;
bool f_restrict;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public UnificationNWay(ParseControl ctrl)
#if !DEBUG
: this()
#endif
{
#if DEBUG
UnificationNWay.unwy = this;
#endif
this.ctrl = ctrl;
this.tm = ctrl.tm;
this.participants = new Tfs[MaxParticipants];
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// n-way unification : locates and traverses a unilateral, type-compatible monotonic descent, if one exists, through
/// any number of internally coreferenced typed feature structures (TFS).
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public bool _n_way(NwayArgs* pnwa_in, int m_dst, bool f_write)
{
Debug.Assert((pnwa_in->f & Edge.Flag.EtmNonBareType) != 0);
Debug.Assert(pnwa_in->c_args > 0);
int j, k, i_arg, c_args = pnwa_in->c_args;
TfsixMark* ptm;
#if NODE_INDEX
NodeIndexEntry** rgnie = stackalloc NodeIndexEntry*[c_args];
if (f_any_ni)
{
for (i_arg = 0; i_arg < c_args; i_arg++)
{
ptm = (TfsixMark*)pnwa_in->tmx + i_arg;
NodeIndex* pni = pp_ni_ptx[ptm->tfsix];
if (pni != null)
{
j = ptm->mark + pni->add_to_mark;
if (j >= 0 && j < pni->c_limit)
rgnie[i_arg] = ((NodeIndexEntry**)(pni + 1))[j];
}
}
}
#endif
ushort* pfix = rgpfix[(int)(pnwa_in->f & Edge.Flag.MultiIdMask)], pfix_end = *pfix++ + pfix;
while (pfix < pfix_end)
{
ushort i_feat = *pfix++;
bool f_prune = del_daughter_map != null && del_daughter_map[i_feat] == 1;
bool f_expand = false;
bool f_coref = false;
NwayArgs** ppa_cur;
NwayArgs nwa;
//NwayArgs* pa = pa_next;
NwayArgs* pa = &nwa;
pa->f = 0;
pa->c_remain = pa->c_args = 0;
pa->fm_first = null;
for (i_arg = 0; i_arg < c_args; i_arg++)
{
short tfsix;
Tfs tfs;
Edge.Flag nf;
int nm;
#if NODE_INDEX
NodeIndexEntry* pnie;
if (f_any_ni && (pnie = rgnie[i_arg]) != null)
{
try_again:
if (pnie->i_feat > i_feat)
continue;
if (pnie->i_feat < i_feat)
{
rgnie[i_arg] = ++pnie;
goto try_again;
}
if ((nf = pnie->f) == 0)
continue;
nm = pnie->out_mark;
tfsix = ((TfsixMark*)pnwa_in->tmx + i_arg)->tfsix;
tfs = null;
}
else
#endif
{
ptm = (TfsixMark*)pnwa_in->tmx + i_arg;
tfsix = ptm->tfsix;
tfs = participants[tfsix];
if ((nf = tfs.TryGetFlagsMark(i_feat, ptm->mark, out nm)) == 0)
continue;
}
nf &= ~(Edge.Flag.Coreference | Edge.Flag.PrunedDuringParsing);
if (pa->f == 0 || pa->f == nf)
{
pa->f = nf;
f_expand = false;
}
else if (nf != 0)
{
Edge.Flag prev = pa->f;
pa->f = tm.UnifyTypesNoCoref(prev, nf);
if (pa->f == Edge.Flag.Bottom)
return false;
if (pa->f == nf)
f_expand = false;
else if (pa->f != prev)
f_expand = true;
/* note: if (f == prev), retain previous f_expand state */
}
if (nm == 0)
continue;
bool already_have = false;
int txnm = (tfsix << 16) | (ushort)nm;
if (nm < 0)
{
nm = ~nm;
NwayArgs* pa_cur = *(ppa_cur = participant_bases[tfsix] + nm);
if (pa_cur == null)
{
if (tfs == null)
tfs = participants[tfsix];
byte c_remain = (f_restrict ? tfs.coref_tally_map_restricted : tfs.coref_tally_map)[nm];
Debug.Assert(c_remain > 0);
// orphan corefs (m<0) are not added to coref table. thus, it is not an error for
// a negative mark to appear in the nwa
if (c_remain <= 1)
goto coref_bit_false_positive;
if (pa == &nwa)
{
pa = pa_next++;
pa->f = nwa.f;
for (j = 0; j < nwa.c_args; j++)
pa->tmx[pa->c_args++] = nwa.tmx[j];
}
*ppa_cur = pa;
pa->c_remain += c_remain;
}
else
{
if (pa == &nwa)
{
if (!tm.UnifyInTypeNoCoref(ref pa->f, pa_cur->f))
return false;
pa = pa_cur;
pa->f = nwa.f;
for (j = 0; j < nwa.c_args; j++)
pa->tmx[pa->c_args++] = nwa.tmx[j];
}
else if (pa != pa_cur)
{
if (!tm.UnifyInTypeNoCoref(ref pa->f, pa_cur->f))
return false;
if ((k = pa_cur->c_args) > 0)
{
TfsixMark* dst = (TfsixMark*)(pa->tmx + pa->c_args);
TfsixMark* src = (TfsixMark*)pa_cur->tmx;
for (j = 0; j < k; j++)
{
if (src->mark < 0)
participant_bases[src->tfsix][~src->mark] = pa;
*dst++ = *src++;
}
pa->c_args += k;
}
// link FMs from 'pa_cur' to 'pa'
if (pa->fm_first == null)
{
if ((pa->fm_first = pa_cur->fm_first) != null)
{
pa->fm_last = pa_cur->fm_last;
}
}
else if (pa_cur->fm_first != null)
{
coref_links[pa->fm_last - p_ate_base] = pa_cur->fm_first;
pa->fm_last = pa_cur->fm_last;
}
pa->c_remain += pa_cur->c_remain;
pa_cur->c_remain = 0;
}
k = pa->c_args;
for (j = 0; j < k; j++)
if (pa->tmx[j] == txnm)
{
already_have = true;
break;
}
}
Debug.Assert(pa->c_remain > 0);
pa->c_remain--;
f_coref = true;
}
coref_bit_false_positive:
if (!already_have)
pa->tmx[pa->c_args++] = txnm;
if (c_plug_remain > 0)
{
for (PlugPair* pp = plugs; pp < plugs_end; pp++)
{
if (*(int*)&pp->seek == txnm)
{
if (!tm.UnifyInTypeNoCoref(ref pa->f, pp->plug.e.FlagsId & ~Edge.Flag.Coreference))
return false;
*((TfsixMark*)pa->tmx + pa->c_args++) = new TfsixMark(pp->plug);
*(int*)&pp->seek = -1;
c_plug_remain--;
break;
}
}
}
}
bool f_nonbare = (pa->f & Edge.Flag.EtmNonBareType) != 0;
if (f_expand && f_nonbare)
{
Tfs tfs_exp = tm.type_arr[(int)(pa->f & Edge.Flag.MultiIdMask)].Expanded;
Debug.Assert(!(tfs_exp is BareTfs));
((TfsixMark*)pa->tmx)[pa->c_args++] = new TfsixMark(AddTfs(tfs_exp), tfs_exp.Edge.Mark);
}
if (f_write)
{
if (f_prune)
{
if (f_nonbare && (pa->f != 0 || f_coref))
{
if (!_n_way(pa, 0, false))
return false;
}
/// outbound now with success below, so we've noted the corefs below and we can now prune the node.
if (m_dst < min_coref_in_mark) min_coref_in_mark = m_dst;
p_ate->i_feat = i_feat;
p_ate->mark = m_dst;
#if CHECK_TOPMOST_FEAT
if (m_dst == 1)
c_fix_topmost--;
#endif
p_ate->e = Edge.PrunedEdge;
p_ate++;
goto recover;
}
/// only write the 'in-' half of a coreferenced node now (that is, during inbound traversal), and add
/// it to the FM queue to have its 'out-' half written with the finished edge when the coreference is
/// complete and outbound. This step cannot be postponed until outbound, because it's possible for
/// the coref to be completed during the traversal below this point
if (f_coref && m_dst != 0)
{
if (m_dst < min_coref_in_mark) min_coref_in_mark = m_dst;
p_ate->i_feat = i_feat;
p_ate->mark = m_dst;
#if CHECK_TOPMOST_FEAT
if (m_dst == 1)
c_fix_topmost--;
#endif
if (pa->fm_first == null)
pa->fm_last = p_ate;
else
coref_links[p_ate - p_ate_base] = pa->fm_first;
pa->fm_first = p_ate;
p_ate++;
}
}
if (pa->c_args == 0 || !f_coref)
{
k = f_nonbare && f_write ? next_mark++ : 0;
if (f_nonbare && !_n_way(pa, k, f_write))
return false;
/// outbound with success below. write a non-coreferenced edge
if (f_write && m_dst != 0 && pa->f != 0)
{
if (m_dst < min_coref_in_mark) min_coref_in_mark = m_dst;
p_ate->i_feat = i_feat;
p_ate->mark = m_dst;
#if CHECK_TOPMOST_FEAT
if (m_dst == 1)
c_fix_topmost--;
#endif
p_ate->e_FlagsId = pa->f;
p_ate->e_Mark = k;
p_ate++;
}
}
else if (pa->c_remain == 0)
{
if (pa->f != 0 || pa->fm_first != pa->fm_last)
{
#if MINIFUNCS
if (!Write(pa, f_nonbare, f_write || pa->fm_first != null))
return false;
#else
/// re-enable writing if we pop out of a coreference which has a FeatMark stored,
/// since that puts us back into a writing area. i.e. must still traverse down until we pop out
pa->mx = 0;
bool fwx = f_write || pa->fm_first != null;
if (fwx)
{
if (pa->fm_first != pa->fm_last)
{
pa->f |= Edge.Flag.Coreference;
pa->mx = next_coref_mark--;
}
else if (f_nonbare)
pa->mx = next_mark++;
}
if (f_nonbare && !_n_way(pa, pa->mx, fwx))
return false;
if (pa->fm_first == null)
continue;
else if (pa->fm_first == pa->fm_last)
pa->fm_first->e_ul = *(ulong*)pa;
else
pa->f_defer = true;
#endif
}
}
else
continue;
recover:
/// wrote. since writing is definitive, check for opportunistic recovery of variadic slots
if (pa->c_remain != DeferredWrite && pa + 1 == pa_next)
{
pa_next--;
pa_next->f = 0;
pa_next->c_remain = pa_next->c_args = 0;
pa_next->fm_first = null;
}
}
return true;
}
#if MINIFUNCS
bool Write(NwayArgs* pa, bool f_nonbare, bool fwx)
{
/// re-enable writing if we pop out of a coreference which has a FeatMark stored,
/// since that puts us back into a writing area. i.e. must still traverse down until we pop out
pa->mx = 0;
if (fwx)
{
if (pa->fm_first != pa->fm_last)
{
pa->f |= Edge.Flag.Coreference;
pa->mx = next_coref_mark--;
}
else if (f_nonbare)
pa->mx = next_mark++;
}
if (f_nonbare && !_n_way(pa, pa->mx, fwx))
return false;
if (pa->fm_first == null)
return true;
else if (pa->fm_first == pa->fm_last)
pa->fm_first->e_ul = *(ulong*)pa;
else
pa->c_remain = int.MinValue;
return true;
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
byte AddTfs(Tfs tfs)
{
//if (c_participants >= MaxParticipants)
// throw new InvalidOperationException("need more participant slots");
TfsSection ts = tfs as TfsSection;
byte[] map = f_restrict ? tfs.coref_tally_map_restricted : tfs.coref_tally_map;
if (map == null)
{
if (ts != null)
{
if (f_restrict)
ts._load_coref_tally_map_restricted();
else
ts._load_coref_tally_map();
}
else
{
int c_corefs = 0;
int max_c = tfs.coref_tally_map.Length;
if (f_restrict)
{
if (max_c > 0 && (tfs.Edge.FlagsId & Edge.Flag.EtmNonBareType) != 0)
{
map = new byte[max_c];
EdgeCounterRestricted ec = new EdgeCounterRestricted(tfs, tfs.Edge, map);
c_corefs = ec.ActualCount;
}
if (c_corefs == 0 || map == null)
map = Tfs.NoCorefsTallyMap;
tfs.coref_tally_map_restricted = map;
}
else
{
if (max_c > 0 && (tfs.Edge.FlagsId & Edge.Flag.EtmNonBareType) != 0)
{
map = new byte[max_c];
EdgeCounter ec = new EdgeCounter(tfs, tfs.Edge, map);
c_corefs = ec.ActualCount;
}
if (c_corefs == 0 || map == null)
map = Tfs.NoCorefsTallyMap;
tfs.coref_tally_map = map;
}
}
}
byte tfsix = c_participants++;
if (ts != null)
tfs = ts.mother;
participants[tfsix] = tfs;
participant_bases[tfsix] = ppmb_next;
ppmb_next += map.Length;
//if (ppmb_next - pp_map_base > CorefsMax)
// throw new InvalidOperationException("need more coref slots");
#if NODE_INDEX
ArrayTfs atfs;
if ((atfs = tfs as ArrayTfs) != null)
{
NodeIndex* pni;
if ((pni = atfs.p_node_index) != null)
{
pp_ni_ptx[tfsix] = pni;
f_any_ni = true;
}
}
#endif
return tfsix;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MotherDaughterTfs _unify_sections(Edge.Flag f, TfsixMark txm_mother, PlugPair* candidates, int count)
{
NwayArgs nwa;
nwa.f = f;
nwa.tmx[0] = *(int*)&txm_mother;
nwa.c_args = 1;
NwayArgs* _p = stackalloc NwayArgs[CorefsMax];
pa_next = _corefs = _p;
arr_tfs_entry* _p_ate = stackalloc arr_tfs_entry[c_edge_alloc];
p_ate = p_ate_base = _p_ate;
arr_tfs_entry** _p_corefs_list = stackalloc arr_tfs_entry*[c_edge_alloc];
coref_links = _p_corefs_list;
#if CHECK_TOPMOST_FEAT
this.c_fix_topmost = *tm.rgpfix[(int)(f & Edge.Flag.MultiIdMask)];
#endif
this.c_plug_remain = count;
this.plugs = candidates;
this.plugs_end = plugs + c_plug_remain;
this.next_coref_mark = -1;
this.next_mark = TopmostMark + 1;
if (!_n_way(&nwa, TopmostMark, true))
return null;
int i_ate = (int)(this.p_ate - _p_ate);
if (i_ate > c_edge_alloc)
throw new Exception();
for (NwayArgs* pa = _corefs; pa < pa_next; pa++)
{
/// Self-blocking structures will result in non-zero tally remainders. The presence of such structure implies
/// unification failure.
if (pa->c_remain > 0)
return null;
else if (pa->c_remain == DeferredWrite)
for (arr_tfs_entry* pfm = pa->fm_first; pfm != null; pfm = _p_corefs_list[pfm - p_ate_base])
pfm->e_ul = *(ulong*)pa;
}
#if CHECK_TOPMOST_FEAT
//Console.Write("{0} ", c_fix_topmost);
// Debug.Assert(c_fix_topmost == 0);
#endif
return new MotherDaughterTfs(
ctrl,
new Edge(f, TopmostMark),
_p_ate,
i_ate,
next_mark,
next_coref_mark,
min_coref_in_mark,
f_restrict);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static MotherDaughterTfs UnifySections(
ParseControl ctrl,
MotherDaughterTfs mother,
int i_first_arg,
Tfs[] candidates,
bool f_args_delete,
bool f_pack_restrict)
{
#if false
if ((f & Edge.Flag.EtmNonBareType) == 0)
throw new NotImplementedException();
#endif
fixed (byte* _del_daughter_map = ctrl.tm.config.parser.deleted_daughter_map)
{
UnificationNWay u = new UnificationNWay(ctrl);
if (f_args_delete)
u.del_daughter_map = _del_daughter_map;
u.rgpfix = (u.f_restrict = f_pack_restrict) ? ctrl.tm.rgpfix_restr : ctrl.tm.rgpfix_allft;
NwayArgs** _pp = stackalloc NwayArgs*[CorefsMax];
u.ppmb_next = u.pp_map_base = _pp;
NwayArgs*** _ppp = stackalloc NwayArgs**[MaxParticipants];
u.participant_bases = _ppp;
#if NODE_INDEX
NodeIndex** _pni = stackalloc NodeIndex*[MaxParticipants];
u.pp_ni_ptx = _pni;
#endif
TfsixMark txm_mother;
txm_mother.tfsix = u.AddTfs(mother);
txm_mother.mark = (short)mother.Edge.Mark;
u.c_edge_alloc = mother.EdgeCount + 100;
PlugPair* sections = stackalloc PlugPair[candidates.Length];
for (int i = 0; i < candidates.Length; i++)
{
PlugPair* p = sections + i;
p->seek.tfsix = txm_mother.tfsix;
p->seek.mark = (short)mother.RuleDaughters[i_first_arg++].Edge.Mark;
Tfs cnd = candidates[i];
p->plug.tfsix = u.AddTfs(cnd);
p->plug.e = cnd.Edge;
u.c_edge_alloc += cnd.EdgeCount;
}
return u._unify_sections(mother.Edge.FlagsId, txm_mother, sections, candidates.Length);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static MotherDaughterTfs UnifySection(
ParseControl ctrl,
TfsSection daughter,
Tfs candidate,
bool f_pruning,
bool f_pack_restrict)
{
MotherDaughterTfs mother = (MotherDaughterTfs)daughter.mother;
Edge.Flag f = mother.Edge.FlagsId;
#if false
if ((f & Edge.Flag.EtmNonBareType) == 0)
throw new NotImplementedException();
#endif
fixed (byte* _del_daughter_map = ctrl.tm.config.parser.deleted_daughter_map)
{
UnificationNWay u = new UnificationNWay(ctrl);
if (f_pruning)
u.del_daughter_map = _del_daughter_map;
u.rgpfix = (u.f_restrict = f_pack_restrict) ? ctrl.tm.rgpfix_restr : ctrl.tm.rgpfix_allft;
NwayArgs** _pp = stackalloc NwayArgs*[CorefsMax];
u.ppmb_next = u.pp_map_base = _pp;
NwayArgs*** _ppp = stackalloc NwayArgs**[MaxParticipants];
u.participant_bases = _ppp;
#if NODE_INDEX
NodeIndex** _pni = stackalloc NodeIndex*[MaxParticipants];
u.pp_ni_ptx = _pni;
#endif
TfsixMark txm_mother;
txm_mother.tfsix = u.AddTfs(mother);
txm_mother.mark = (short)mother.Edge.Mark;
u.c_edge_alloc = mother.EdgeCount + candidate.EdgeCount + 100;
PlugPair pp;
pp.seek.tfsix = txm_mother.tfsix;
pp.seek.mark = (short)daughter.Edge.Mark;
pp.plug.tfsix = u.AddTfs(candidate);
pp.plug.e = candidate.Edge;
return u._unify_sections(f, txm_mother, &pp, 1);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public partial struct NwayArgs
{
public Edge.Flag f;
public int mx;
public arr_tfs_entry* fm_first;
public arr_tfs_entry* fm_last;
public int c_remain;
public int c_args;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public fixed int tmx[ArgArityMax];
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public struct TfsixMark
{
public TfsixMark(short tfsix, int mark)
{
this.tfsix = tfsix;
this.mark = (short)mark;
}
public TfsixMark(TfsixEdge te)
{
this.tfsix = te.tfsix;
this.mark = (short)te.e.Mark;
}
public short mark;
public short tfsix;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
///
/// </summary>
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public struct PlugPair
{
public PlugPair(TfsixMark seek, TfsixEdge plug)
{
this.seek = seek;
this.plug = plug;
}
public TfsixMark seek;
public TfsixEdge plug;
};
};
}