public ComparisonUnitGroup(
            IEnumerable <ComparisonUnit> comparisonUnitList,
            ComparisonUnitGroupType groupType,
            int level)
        {
            Contents = comparisonUnitList.ToList();
            ComparisonUnitGroupType = groupType;
            ComparisonUnit     first = Contents.First();
            ComparisonUnitAtom comparisonUnitAtom = GetFirstComparisonUnitAtomOfGroup(first);

            XElement[] ancestorsToLookAt = comparisonUnitAtom
                                           .AncestorElements
                                           .Where(e => e.Name == W.tbl || e.Name == W.tr || e.Name == W.tc || e.Name == W.p || e.Name == W.txbxContent)
                                           .ToArray();

            XElement ancestor = ancestorsToLookAt[level];

            if (ancestor == null)
            {
                throw new OpenXmlPowerToolsException("Internal error: ComparisonUnitGroup");
            }

            SHA1Hash           = (string)ancestor.Attribute(PtOpenXml.SHA1Hash);
            CorrelatedSHA1Hash = (string)ancestor.Attribute(PtOpenXml.CorrelatedSHA1Hash);
            StructureSHA1Hash  = (string)ancestor.Attribute(PtOpenXml.StructureSHA1Hash);
        }
        private static void AppendAncestorsDump(StringBuilder sb, ComparisonUnitAtom sr)
        {
            string s = sr
                       .AncestorElements.Select(p => p.Name.LocalName + GetUnid(p) + "/")
                       .StringConcatenate()
                       .TrimEnd('/');

            sb.Append("Ancestors:" + s);
        }
        private static void AppendAncestorsUnidsDump(StringBuilder sb, ComparisonUnitAtom sr)
        {
            var zipped = sr.AncestorElements.Zip(sr.AncestorUnids, (a, u) => new
            {
                AncestorElement = a,
                AncestorUnid    = u
            });

            string s = zipped
                       .Select(p => p.AncestorElement.Name.LocalName + "[" + p.AncestorUnid.Substring(0, 8) + "]/")
                       .StringConcatenate().TrimEnd('/');

            sb.Append("Ancestors:" + s);
        }
        private static void CreateComparisonUnitAtomListRecurse(
            OpenXmlPart part,
            XElement element,
            List <ComparisonUnitAtom> comparisonUnitAtomList,
            WmlComparerSettings settings)
        {
            if (element.Name == W.body || element.Name == W.footnote || element.Name == W.endnote)
            {
                foreach (XElement item in element.Elements())
                {
                    CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
                }
                return;
            }

            if (element.Name == W.p)
            {
                IEnumerable <XElement> paraChildrenToProcess = element
                                                               .Elements()
                                                               .Where(e => e.Name != W.pPr);
                foreach (XElement item in paraChildrenToProcess)
                {
                    CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
                }
                XElement paraProps = element.Element(W.pPr);
                if (paraProps == null)
                {
                    var pPrComparisonUnitAtom = new ComparisonUnitAtom(
                        new XElement(W.pPr),
                        element.AncestorsAndSelf()
                        .TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes).Reverse()
                        .ToArray(),
                        part,
                        settings);
                    comparisonUnitAtomList.Add(pPrComparisonUnitAtom);
                }
                else
                {
                    var pPrComparisonUnitAtom = new ComparisonUnitAtom(
                        paraProps,
                        element.AncestorsAndSelf()
                        .TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes).Reverse()
                        .ToArray(),
                        part,
                        settings);
                    comparisonUnitAtomList.Add(pPrComparisonUnitAtom);
                }

                return;
            }

            if (element.Name == W.r)
            {
                IEnumerable <XElement> runChildrenToProcess = element
                                                              .Elements()
                                                              .Where(e => e.Name != W.rPr);
                foreach (XElement item in runChildrenToProcess)
                {
                    CreateComparisonUnitAtomListRecurse(part, item, comparisonUnitAtomList, settings);
                }
                return;
            }

            if (element.Name == W.t || element.Name == W.delText)
            {
                string val = element.Value;
                foreach (char ch in val)
                {
                    var sr = new ComparisonUnitAtom(
                        new XElement(element.Name, ch),
                        element.AncestorsAndSelf()
                        .TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes).Reverse()
                        .ToArray(),
                        part,
                        settings);
                    comparisonUnitAtomList.Add(sr);
                }

                return;
            }

            if (AllowableRunChildren.Contains(element.Name) || element.Name == W._object)
            {
                var sr3 = new ComparisonUnitAtom(
                    element,
                    element.AncestorsAndSelf().TakeWhile(a => a.Name != W.body && a.Name != W.footnotes && a.Name != W.endnotes)
                    .Reverse().ToArray(),
                    part,
                    settings);
                comparisonUnitAtomList.Add(sr3);
                return;
            }

            RecursionInfo re = RecursionElements.FirstOrDefault(z => z.ElementName == element.Name);

            if (re != null)
            {
                AnnotateElementWithProps(part, element, comparisonUnitAtomList, re.ChildElementPropertyNames, settings);
                return;
            }

            if (ElementsToThrowAway.Contains(element.Name))
            {
                return;
            }

            AnnotateElementWithProps(part, element, comparisonUnitAtomList, null, settings);
        }
        // The following method must be made internal if we ever turn this part of the partial class
        // into its own class.
        private static ComparisonUnit[] GetComparisonUnitList(
            ComparisonUnitAtom[] comparisonUnitAtomList,
            WmlComparerSettings settings)
        {
            var seed = new Atgbw
            {
                Key = null,
                ComparisonUnitAtomMember = null,
                NextIndex = 0
            };

            IEnumerable <Atgbw> groupingKey = comparisonUnitAtomList
                                              .Rollup(seed, (sr, prevAtgbw, i) =>
            {
                int?key;
                int nextIndex = prevAtgbw.NextIndex;
                if (sr.ContentElement.Name == W.t)
                {
                    string chr = sr.ContentElement.Value;
                    char ch    = chr[0];
                    if (ch == '.' || ch == ',')
                    {
                        var beforeIsDigit = false;
                        if (i > 0)
                        {
                            ComparisonUnitAtom prev = comparisonUnitAtomList[i - 1];
                            if (prev.ContentElement.Name == W.t && char.IsDigit(prev.ContentElement.Value[0]))
                            {
                                beforeIsDigit = true;
                            }
                        }

                        var afterIsDigit = false;
                        if (i < comparisonUnitAtomList.Length - 1)
                        {
                            ComparisonUnitAtom next = comparisonUnitAtomList[i + 1];
                            if (next.ContentElement.Name == W.t && char.IsDigit(next.ContentElement.Value[0]))
                            {
                                afterIsDigit = true;
                            }
                        }

                        if (beforeIsDigit || afterIsDigit)
                        {
                            key = nextIndex;
                        }
                        else
                        {
                            nextIndex++;
                            key = nextIndex;
                            nextIndex++;
                        }
                    }
                    else if (settings.WordSeparators.Contains(ch))
                    {
                        nextIndex++;
                        key = nextIndex;
                        nextIndex++;
                    }
                    else
                    {
                        key = nextIndex;
                    }
                }
                else if (WordBreakElements.Contains(sr.ContentElement.Name))
                {
                    nextIndex++;
                    key = nextIndex;
                    nextIndex++;
                }
                else
                {
                    key = nextIndex;
                }

                return(new Atgbw
                {
                    Key = key,
                    ComparisonUnitAtomMember = sr,
                    NextIndex = nextIndex
                });
            })
                                              .ToArray();

            if (False)
            {
                var sb = new StringBuilder();
                foreach (Atgbw item in groupingKey)
                {
                    sb.Append(item.Key + Environment.NewLine);
                    sb.Append("    " + item.ComparisonUnitAtomMember.ToString(0) + Environment.NewLine);
                }

                string sbs = sb.ToString();
                TestUtil.NotePad(sbs);
            }

            IEnumerable <IGrouping <int?, Atgbw> > groupedByWords = groupingKey
                                                                    .GroupAdjacent(gc => gc.Key)
                                                                    .ToArray();

            if (False)
            {
                var sb = new StringBuilder();
                foreach (IGrouping <int?, Atgbw> group in groupedByWords)
                {
                    sb.Append("Group ===== " + @group.Key + Environment.NewLine);
                    foreach (Atgbw gc in @group)
                    {
                        sb.Append("    " + gc.ComparisonUnitAtomMember.ToString(0) + Environment.NewLine);
                    }
                }

                string sbs = sb.ToString();
                TestUtil.NotePad(sbs);
            }

            WithHierarchicalGroupingKey[] withHierarchicalGroupingKey = groupedByWords
                                                                        .Select(g =>
            {
                string[] hierarchicalGroupingArray = g
                                                     .First()
                                                     .ComparisonUnitAtomMember
                                                     .AncestorElements
                                                     .Where(a => ComparisonGroupingElements.Contains(a.Name))
                                                     .Select(a => a.Name.LocalName + ":" + (string)a.Attribute(PtOpenXml.Unid))
                                                     .ToArray();

                return(new WithHierarchicalGroupingKey
                {
                    ComparisonUnitWord = new ComparisonUnitWord(g.Select(gc => gc.ComparisonUnitAtomMember)),
                    HierarchicalGroupingArray = hierarchicalGroupingArray
                });
            }
                                                                                )
                                                                        .ToArray();

            if (False)
            {
                var sb = new StringBuilder();
                foreach (WithHierarchicalGroupingKey group in withHierarchicalGroupingKey)
                {
                    sb.Append("Grouping Array: " +
                              @group.HierarchicalGroupingArray.Select(gam => gam + " - ").StringConcatenate() +
                              Environment.NewLine);
                    foreach (ComparisonUnit gc in @group.ComparisonUnitWord.Contents)
                    {
                        sb.Append("    " + gc.ToString(0) + Environment.NewLine);
                    }
                }

                string sbs = sb.ToString();
                TestUtil.NotePad(sbs);
            }

            ComparisonUnit[] cul = GetHierarchicalComparisonUnits(withHierarchicalGroupingKey, 0).ToArray();

            if (False)
            {
                string str = ComparisonUnit.ComparisonUnitListToString(cul);
                TestUtil.NotePad(str);
            }

            return(cul);
        }
 private static string PadLocalName(int xNamePad, ComparisonUnitAtom item)
 {
     return((item.ContentElement.Name.LocalName + " ").PadRight(xNamePad, '-') + " ");
 }