using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using miew.BitArray; namespace miew.Dag { using String = System.String; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public class Dag<T> : IList<T>, INotifyPropertyChanged where T : Dag<T>.Node { T[] rg_nodes; HashSet<T> hs_nodes = new HashSet<T>(); IEnumerable<T> nodes { get { return (IEnumerable<T>)rg_nodes ?? hs_nodes; } } HashSet<T> tops = new HashSet<T>(); HashSet<T> leaves = new HashSet<T>(); bool f_cycle_prevention = false; bool f_index_ok = true; int max_level = -1; //bool f_levels_ok = true; //internal void CheckNodeLevels() //{ // if (!f_levels_ok) // { // foreach (Node n in this) // n._set_level(-1); // f_levels_ok = true; // Debug.WriteLine(new StackTrace().ToString()); // } //} static int nest = 0; internal void ComputeNodeLevels() { var old_vals = nodes.Select(x => x._set_level(-1)).ToArray(); var new_vals = nodes.Select(x => x.Level).ToArray(); max_level = new_vals.Max(); int i = 0; nest++; Debug.Print("---"); foreach (Node n in nodes) { int ov = old_vals[i]; if (ov != -1 && ov != new_vals[i]) { Debug.Print("{0} node {1}: {2} -> {3}", nest, n.Index, old_vals[i], new_vals[i]); n.NotifyPropertyChanged("Level"); } i++; } Debug.Print("---"); nest--; } [DebuggerDisplay("{max_level==-1?\"not computed\":max_level.ToString()}")] public int MaxLevel { get { //CheckNodeLevels(); if (max_level == -1) { //int lvl; //foreach (T t in nodes) // if ((lvl = t.Level) > max_level) // max_level = lvl; ComputeNodeLevels(); } return max_level; } } void SwitchToHash() { if (hs_nodes == null) { hs_nodes = new HashSet<T>(rg_nodes); rg_nodes = null; } } void SwitchToArray() { if (rg_nodes == null) { bool f_tmp = f_index_ok; f_index_ok = true; rg_nodes = hs_nodes.OrderBy(t => t.Index).ToArray(); hs_nodes = null; if (!f_tmp) { for (int i = 0; i < rg_nodes.Length; i++) rg_nodes[i]._set_index(i); } } } public HashSet<T> Tops { get { return tops; } } public HashSet<T> Leaves { get { return leaves; } } public bool CyclePrevention { get { return f_cycle_prevention; } set { f_cycle_prevention = value; } } //public HashSet<T> AsHashSet() //{ // SwitchToHash(); // return hs_nodes; //} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [DebuggerDisplay("{ToString(),nq}")] public abstract class Node : INotifyPropertyChanged { [DebuggerBrowsable(DebuggerBrowsableState.Never)] T This { get { return (T)this; } } Node(Dag<T> dag, int level) { this.dag = dag; this.level = level; this.index = dag.Count; dag.Add(This); dag.tops.Add(This); dag.leaves.Add(This); } public Node(Dag<T> dag) : this(dag, 0) { } internal Node(Dag<T> dag, int level, BitArr code, Flags flags) : this(dag, level) { this.m_code = code; this.flags |= flags; } [Flags] public enum Flags { GlbType = 0x00000004, }; int index; readonly Dag<T> dag; readonly HashSet<T> parents = new HashSet<T>(); readonly HashSet<T> children = new HashSet<T>(); int id; int level; Flags flags; BitArr m_code; internal void _set_index(int i) { index = i; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public Dag<T> Dag { get { return dag; } } public bool IsTop { get { return parents.Count == 0; } } public bool IsLeaf { get { return children.Count == 0; } } public bool IsGlbType { get { return (flags & Flags.GlbType) > 0; } } public void AddParent(T parent) { if (parent == this || parents.Contains(parent) || (dag.f_cycle_prevention && AllDescendants.Contains(parent))) return; //if (parent.level < this.level - 1) // parent.LocalTop.reset_subtree_levels(); //else if (this.level < parent.level + 1) // this.LocalTop.reset_subtree_levels(); parents.Add(parent); parent.children.Add(This); if (parents.Count == 1) dag.tops.Remove(This); if (parent.children.Count == 1) dag.leaves.Remove(parent); //dag.f_levels_ok = false; dag.ComputeNodeLevels(); /// -- Notification barrier point -- if (parents.Count == 1) NotifyPropertyChanged("IsTop"); if (parent.children.Count == 1) parent.NotifyPropertyChanged("IsLeaf"); NotifyPropertyChanged("Parent.Item"); parent.NotifyPropertyChanged("Child.Item"); } public void AddChild(T child) { if (child == this || children.Contains(child) || (dag.f_cycle_prevention && AllAncestors.Contains(child))) return; //if (child.level < this.level + 1) // child.LocalTop.reset_subtree_levels(); //else if (this.level < child.level - 1) // this.LocalTop.reset_subtree_levels(); children.Add(child); child.parents.Add(This); if (children.Count == 1) dag.leaves.Remove(This); if (child.parents.Count == 1) dag.tops.Remove(child); //dag.f_levels_ok = false; dag.ComputeNodeLevels(); /// -- Notification barrier point -- if (children.Count == 1) NotifyPropertyChanged("IsLeaf"); if (child.parents.Count == 1) child.NotifyPropertyChanged("IsTop"); NotifyPropertyChanged("Child.Item"); child.NotifyPropertyChanged("Parent.Item"); } public void RemoveParent(T parent) { if (parent == this || !parents.Remove(parent)) return; parent.children.Remove(This); if (parents.Count == 0) dag.tops.Add(This); if (parent.children.Count == 0) dag.leaves.Add(parent); //dag.f_levels_ok = false; dag.ComputeNodeLevels(); /// -- Notification barrier point -- if (parents.Count == 0) NotifyPropertyChanged("IsTop"); if (parent.children.Count == 0) parent.NotifyPropertyChanged("IsLeaf"); NotifyPropertyChanged("Parent.Item"); parent.NotifyPropertyChanged("Child.Item"); } public void RemoveChild(T child) { if (child == this || !children.Remove(child)) return; child.parents.Remove(This); if (children.Count == 0) dag.leaves.Add(This); if (child.parents.Count == 0) dag.tops.Add(child); //dag.f_levels_ok = false; dag.ComputeNodeLevels(); /// -- Notification barrier point -- if (children.Count == 0) NotifyPropertyChanged("IsLeaf"); if (child.parents.Count == 0) child.NotifyPropertyChanged("IsTop"); NotifyPropertyChanged("Child.Item"); child.NotifyPropertyChanged("Parent.Item"); } public void RemoveAllChildren() { List<KeyValuePair<Node, String>> notifications = null; foreach (T child in children) { if (child.parents.Remove(This)) { notifications = notifications ?? new List<KeyValuePair<Node, String>>(); notifications.Add(new KeyValuePair<Node, string>(child, "Parent.Item")); if (child.parents.Count == 0) notifications.Add(new KeyValuePair<Node, String>(child, "IsTop")); } } if (notifications == null) return; children.Clear(); dag.ComputeNodeLevels(); //dag.f_levels_ok = false; /// -- Notification barrier point -- foreach (var kvp in notifications) kvp.Key.NotifyPropertyChanged(kvp.Value); this.NotifyPropertyChanged("IsLeaf"); this.NotifyPropertyChanged("Child.Item"); } public void RemoveAllParents() { List<KeyValuePair<Node, String>> notifications = null; foreach (T parent in parents) { if (parent.children.Remove(This)) { notifications = notifications ?? new List<KeyValuePair<Node, String>>(); notifications.Add(new KeyValuePair<Node, string>(parent, "Child.Item")); if (parent.children.Count == 0) notifications.Add(new KeyValuePair<Node, String>(parent, "IsLeaf")); } } if (notifications == null) return; parents.Clear(); dag.ComputeNodeLevels(); //dag.f_levels_ok = false; /// -- Notification barrier point -- foreach (var kvp in notifications) kvp.Key.NotifyPropertyChanged(kvp.Value); this.NotifyPropertyChanged("IsTop"); this.NotifyPropertyChanged("Parent.Item"); } public HashSet<T> Parents { get { return parents; } } public HashSet<T> Children { get { return children; } } public IEnumerable<T> NextLevelChildren { get { return Children.Where(cn => cn.Level == Level + 1); } } public IEnumerable<T> PreviousLevelParents { get { return Parents.Where(cn => cn.Level == Level - 1); } } public HashSet<T> AllAncestors { get { HashSet<T> hs = new HashSet<T>(); AllAncestorsInclusive(hs); hs.Remove(This); return hs; } } public void AllAncestorsInclusive(HashSet<T> hs) { foreach (T t in parents) if (!hs.Contains(t)) t.AllAncestorsInclusive(hs); hs.Add(This); } public HashSet<T> AllDescendants { get { HashSet<T> hs = new HashSet<T>(); AllDescendantsInclusive(hs); hs.Remove(This); return hs; } } public void AllDescendantsInclusive(HashSet<T> hs) { foreach (T t in children) if (!hs.Contains(t)) t.AllDescendantsInclusive(hs); hs.Add(This); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public BitArr Code { get { return m_code; } set { m_code = value; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public int Id { get { return id; } set { id = value; } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] public int Index { get { if (dag.hs_nodes != null && !dag.f_index_ok) dag.SwitchToArray(); return index; } } //void DownwardsLevel() //{ // if (parents.Count == 0) // { // level = 0; // return; // } //} //Node LocalTop { get { return IsTop ? this : parents.First().LocalTop; } } //void reset_subtree_levels() //{ // foreach (Node child in children) // child.reset_subtree_levels(); // level = -1; //} //void SetLocalTopLevel(ref int i) //{ // if (parents.Count == 0) // { // reset_subtree_levels(); // level = i; // } // else // { // i++; // parents.ArgMax(p => p.level).SetLocalTopLevel(ref i); // } //} [DebuggerDisplay("{level==-1?\"not computed\":level.ToString()}")] public int Level { get { //dag.CheckNodeLevels(); if (level == -1) { level = IsTop ? 0 : parents.Max(p => p.Level) + 1; //NotifyPropertyChanged("Level"); } return level; } } internal int _set_level(int new_val) { int old_val = level; if (level != new_val) { level = new_val; if (level != -1) NotifyPropertyChanged("Level"); } return old_val; } public override string ToString() { return String.Format("level: {0} {1}", level == -1 ? "(not computed)" : level.ToString(), flags); } public event PropertyChangedEventHandler PropertyChanged; internal void NotifyPropertyChanged(String s_field) { var h = PropertyChanged; if (h != null) h(this, new PropertyChangedEventArgs(s_field)); } }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// EmbedBcpo /// /// Embed the type hierarchy in a bounded complete partial order (BCPO) lattice by inserting greatest- /// lower bound (GLB) types as required. /// /// References: /// Hassan Ait-Kaci, Robert Boyer, Patrick Lincoln, Roger Nasr. 1989. "Efficient Implementation of Lattice /// Operations" /// Ulrich Callmeier. 2001. "Efficient Parsing with Large-Scale Unification Grammars" p.28-30. /// Gerald Penn. 2000. "The Algebraic Structure of Attributed Type Signatures" p. 30-34 /// Bernd Kiefer, Hans-Ulrich Krieger, John Carroll, Rob Malouf. "A Bag of Useful Techniques for Efficient /// and Robust Parsing" p. 475. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// int code_size; int c_types; public Dictionary<BitArr, T> code_dict; public void EmbedBcpo(Func<T> glb_factory) { // retain the number of types prior to adding GLBs code_size = Count; //// check for inheritance from *top* and assign a maximal-depth value to each node //foreach (DagNode t in entries.Keys) // t.Level; // generate Aït-Kaci transitive-closure codes for all user-specified types, in order according to // the depth values just determined. code_dict = new Dictionary<BitArr, T>(); T[] bitpos2type = new T[code_size]; int cur_code = code_size - 1; foreach (T tt in nodes.OrderByDescending(e => e.Level)) { BitArr code = new BitArr(code_size); code[cur_code] = true; foreach (T tc in tt.Children) code.OrEq(tc.Code); tt.Code = code; code_dict.Add(code, tt); bitpos2type[cur_code] = tt; cur_code--; } /// determine codes for the GLB types which will be needed to close the graph T[] rg_tfs = nodes.Where(t => !t.IsLeaf).ToArray(); while (true) { List<T> added_glbs = new List<T>(); for (int i = 0; i < rg_tfs.Length; i++) { T t0 = rg_tfs[i]; BitArr ba0 = t0.Code; for (int j = i + 1; j < rg_tfs.Length; j++) { T t1 = rg_tfs[j]; // todo: fast test and with hash at once if (ba0.FastTest(t1.Code)) { BitArr z = ba0.AndWithHash(t1.Code); int thislevel = t0.Level; if (t1.Level > thislevel) thislevel = t1.Level; T glbt; if (code_dict.TryGetValue(z, out glbt)) { if (thislevel > glbt.Level) glbt._set_level(thislevel); } else { //glbt = new T(this, thislevel, z, Node.Flags.GlbType); glbt = glb_factory(); code_dict.Add(z, glbt); added_glbs.Add(glbt); } } } } if (added_glbs.Count == 0) break; rg_tfs = added_glbs.ToArray(); } c_types = code_dict.Count; if (c_types > ushort.MaxValue) throw new Exception(String.Format("System supports a maximum of {0} types and {1} types were specified.", ushort.MaxValue, c_types)); /// sort all types including the new GLB types into a certain topological order T[] type_arr = code_dict .Values .OrderBy(e => e.Level) .ThenByDescending(e => e.Code.OnesCount()) .ToArray(); /// create a set of negations for all the codes, for use in step 1. BitArr[] notcodes = new BitArr[c_types]; for (int i = 0; i < c_types; i++) notcodes[i] = ~type_arr[i].Code; int ops = 0; List<T> draft = new List<T>(); BitArr r = new BitArr(code_size); for (int i = c_types - 1; i > 0; i--) { T node = type_arr[i]; /// node ids correspond to their index in 'type_arr'. Set it now. node.Id = i; /// Looking only at GLBs, we can directly obtain the transitive closure of the graph by carefully manipulating /// their parents and children. if (!node.IsGlbType) continue; /// 1. This GLB as a parent: add its children /// 1a. get a list of draft candidates: all descendants of 'node' /// The order in which these are added is important because it determines the order of testing children /// in step 1b. It's just a topological order. The list is needed because children may be eliminated from /// the list before the point at which they'd otherwise be added. draft.Clear(); for (int j = i + 1; j < c_types; j++) { T upper = type_arr[j]; if (!upper.Code.FastTest(notcodes[i])) draft.Add(upper); } /// 1b. pick the subset of immediate children from this list /// While the list is not empty, add the oldest item as a child and then remove all of its descendants from the /// list. while (draft.Count > 0) { T new_child = draft[0]; draft.RemoveAt(0); if (!new_child.IsGlbType) foreach (T par in new_child.Parents.ToArray()) { ops++; if (node.Code.AndGivesExact(par.Code)) new_child.RemoveParent(par); } node.AddChild(new_child); for (int q = 0; q < draft.Count; q++) { ops++; if (draft[q].Code.AndGivesExact(new_child.Code)) draft.RemoveAt(q--); } } /// 2. This GLB as a child: add its parents /// 2a. get a list of draft candidates between 'node' and *top* draft.Clear(); BitArr nc = node.Code; for (int j = i - 1; j >= 0; j--) { T lower = type_arr[j]; if (!lower.IsLeaf) { ops++; if (nc.AndGivesExact(lower.Code)) draft.Add(lower); } } /// 2b. pick a minimal set of parents from this list. /// Each selection allows us to eliminate others others from the list. This effect is maximized by starting /// with candidates with the fewest additional bits set beyond the required match. This was the reason for /// the secondary sort on 'arr' above. r.CopyFrom(nc); for (int j = 0; j < draft.Count; j++) { T lower = draft[j]; if (lower.Code.FastTestNotArg(r)) { r.OrEq(lower.Code); for (int k = j + 1; k < draft.Count; k++) { ops++; if (lower.Code.AndGivesExact(draft[k].Code)) draft.RemoveAt(k--); } // all GLB-to-GLB edges are all added as children, above if (!lower.IsGlbType) { foreach (uint bit_pos in nc.OnesPositions()) lower.RemoveChild(bitpos2type[bit_pos]); node.AddParent(lower); } } } } Console.WriteLine("types {0} closed {1} glb {2} ops {3}", code_size, type_arr.Length, type_arr.Length - code_size, ops); } public int Count { get { return rg_nodes != null ? rg_nodes.Length : hs_nodes.Count; } } public int IndexOf(T item) { SwitchToArray(); return System.Array.IndexOf<T>(rg_nodes, item); } public void Insert(int index, T item) { throw new NotImplementedException(); } public void RemoveAt(int index) { throw new NotImplementedException(); } public T this[int index] { get { SwitchToArray(); return rg_nodes[index]; } set { throw new NotImplementedException(); } } public void Add(T node) { SwitchToHash(); hs_nodes.Add(node); } public void Clear() { rg_nodes = null; hs_nodes = new HashSet<T>(); } public bool Contains(T item) { if (hs_nodes != null) return hs_nodes.Contains(item); return System.Array.IndexOf<T>(rg_nodes, item) != -1; } public void CopyTo(T[] array, int arrayIndex) { foreach (T t in nodes) array[arrayIndex++] = t; } public bool IsReadOnly { get { return false; } } public bool Remove(T item) { SwitchToHash(); if (!hs_nodes.Contains(item)) return false; item.RemoveAllChildren(); item.RemoveAllParents(); hs_nodes.Remove(item); f_index_ok = false; return true; } public IEnumerator<T> GetEnumerator() { return nodes.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } public event PropertyChangedEventHandler PropertyChanged; void NotifyPropertyChanged(String s_field) { var h = PropertyChanged; if (h != null) h(this, new PropertyChangedEventArgs(s_field)); } }; }