// Initialize our dictionary class variable with [element, EID] for all elements in all dynamic streams in depot.
        // AcUtilsException caught and logged in %LOCALAPPDATA%\AcTools\Logs\EvilTwins-YYYY-MM-DD.log on stat command failure.
        // Exception caught and logged in same for a range of exceptions.
        private static async Task <bool> initMapAsync(AcDepot depot)
        {
            bool ret = false; // assume failure

            try
            {
                IEnumerable <AcStream> filter = from n in depot.Streams
                                                where n.IsDynamic && !n.Hidden
                                                select n;
                List <Task <AcResult> > tasks = new List <Task <AcResult> >(filter.Count());
                foreach (AcStream stream in filter)
                {
                    tasks.Add(AcCommand.runAsync($@"stat -a -s ""{stream}"" -fx")); // -a for all elements in stream
                }
                AcResult[] arr = await Task.WhenAll(tasks);                         // finish running stat commands in parallel

                if (arr != null && arr.All(n => n.RetVal == 0))                     // true if all initialized successfully
                {
                    HashSet <Tuple <string, int> > hset = new HashSet <Tuple <string, int> >(_tcompare);
                    foreach (AcResult r in arr)
                    {
                        // if empty the stream has an ACL that is preventing us from reading it or some other error occurred
                        if (r == null || String.IsNullOrEmpty(r.CmdResult))
                        {
                            continue;
                        }
                        XElement xml = XElement.Parse(r.CmdResult);
                        foreach (XElement e in xml.Elements("element"))
                        {
                            string path = (string)e.Attribute("location");
                            int    eid  = (int)e.Attribute("id");
                            hset.Add(new Tuple <string, int>(path, eid));
                        }
                    }

                    lock (_locker) { _map.Add(depot, hset); }
                    ret = true; // operation succeeded
                }
            }

            catch (AcUtilsException ecx)
            {
                AcDebug.Log($"AcUtilsException caught and logged in Program.initMapAsync{Environment.NewLine}{ecx.Message}");
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in Program.initMapAsync{Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
        // Run the stat command for all dynamic streams in depot and initialize our dictionary class variable
        // with the xlinked elements found that have a type listed in ElementTypes from XLinked.exe.config.
        // Returns true if the operation succeeded, false otherwise. AcUtilsException caught and logged in
        // %LOCALAPPDATA%\AcTools\Logs\XLinked-YYYY-MM-DD.log on stat command failure. Exception caught and
        // logged in same for a range of exceptions.
        private static async Task <bool> initAsync(AcDepot depot)
        {
            bool ret = false; // assume failure

            try
            {
                int num = depot.Streams.Count();
                List <Task <AcResult> > tasks = new List <Task <AcResult> >(num);
                foreach (AcStream stream in depot.Streams)
                {
                    // -k: Display the element type (that is, data type) of this version
                    // -v: Display the target of an element link or symbolic link
                    tasks.Add(AcCommand.runAsync($@"stat -s ""{stream}"" -a -fkvx"));
                }

                HashSet <XElement> hset = new HashSet <XElement>(_comparer);
                while (tasks.Count > 0)
                {
                    Task <AcResult> r = await Task.WhenAny(tasks);

                    tasks.Remove(r);
                    if (r == null || r.Result.RetVal != 0)
                    {
                        return(false);
                    }
                    XElement xml = XElement.Parse(r.Result.CmdResult);
                    foreach (XElement e in xml.Elements("element")
                             // attribute xlinked="true" exists only when status includes (xlinked),
                             // otherwise it isn't there (i.e. there never is an xlinked="false" in the XML)
                             .Where(n => (string)n.Attribute("xlinked") != null &&
                                    _etypes.Any(t => t == n.acxType("elemType"))))
                    {
                        hset.Add(e);
                    }
                }

                lock (_locker) { _map.Add(depot, hset); }
                ret = true;
            }

            catch (AcUtilsException exc)
            {
                AcDebug.Log($"AcUtilsException caught and logged in Program.initAsync{Environment.NewLine}{exc.Message}");
            }
            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in Program.initAsync{Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }
        // Report evil twins if found. Assumes the initMapAsync method has been called. Exception caught
        // and logged in %LOCALAPPDATA%\AcTools\Logs\EvilTwins-YYYY-MM-DD.log on operation failure.
        private static async Task <bool> reportAsync()
        {
            bool ret = false; // assume failure

            try
            {
                foreach (KeyValuePair <AcDepot, HashSet <Tuple <string, int> > > pair in _map.OrderBy(n => n.Key)) // order by AcDepot
                {
                    AcDepot depot = pair.Key;                                                                      // depot
                    HashSet <Tuple <string, int> > hset = pair.Value;                                              // [element, EID] from all dynamic streams in depot
                    // from our hashset create a collection of elements mapped to their EID's
                    Lookup <string, Tuple <string, int> > col = (Lookup <string, Tuple <string, int> >)hset.ToLookup(n => n.Item1);
                    // where more than one EID exists for the element, order by element
                    foreach (var ii in col.Where(n => n.Count() > 1).OrderBy(n => n.Key))
                    {
                        string element = ii.Key;
                        if (_excludeList != null && _excludeList.Contains(element))
                        {
                            continue; // ignore if in TwinsExcludeFile
                        }
                        log(element);
                        IEnumerable <AcStream> filter = from n in depot.Streams
                                                        where n.IsDynamic && !n.Hidden
                                                        select n;
                        List <Task <XElement> > tasks = new List <Task <XElement> >(filter.Count());
                        foreach (AcStream stream in filter)
                        {
                            tasks.Add(getElementInfoAsync(stream, element));
                        }

                        XElement[] arr = await Task.WhenAll(tasks);                      // finish running stat commands in parallel

                        if (arr != null && arr.All(n => n != null))                      // true if all ran successfully
                        {
                            foreach (Tuple <string, int> jj in ii.OrderBy(n => n.Item2)) // order twins by EID
                            {
                                int eid = jj.Item2;
                                log($"\tEID: {eid} on {DateTime.Now.ToString("ddd MMM d h:mm tt")}");
                                // C# language short-circuit: the test for id equals eid isn't evaluated if status equals "(no such elem)",
                                // otherwise an exception would be thrown since the id attribute doesn't exist in this case
                                foreach (XElement e in arr.Where(n => (string)n.Attribute("status") != "(no such elem)" &&
                                                                 (int)n.Attribute("id") == eid).OrderBy(n => n.Annotation <AcStream>()))
                                {
                                    log($"\t\t{e.Annotation<AcStream>()} {(string)e.Attribute("status")}");
                                    string      namedVersion = (string)e.Attribute("namedVersion"); // virtual stream name and version number
                                    string      temp         = (string)e.Attribute("Real");
                                    string[]    real         = temp.Split('\\');                    // workspace stream and version numbers
                                    AcStream    wkspace      = depot.getStream(int.Parse(real[0])); // workspace stream
                                    ElementType elemType     = e.acxType("elemType");
                                    string      twin;
                                    if ((long?)e.Attribute("size") != null)
                                    {
                                        twin = $"\t\t\tSize: {(long)e.Attribute("size")}, ModTime: {e.acxTime("modTime")} {{{elemType}}}" +
                                               $"{Environment.NewLine}\t\t\tReal: {wkspace}\\{real[1]}, Virtual: {namedVersion}";
                                    }
                                    else // a folder or link
                                    {
                                        twin = $"\t\t\tReal: {wkspace}\\{real[1]}, Virtual: {namedVersion} {{{elemType}}}";
                                    }

                                    log(twin);
                                }

                                log("");
                            }
                        }
                    }
                }

                ret = true; // operation succeeded
            }

            catch (Exception ecx)
            {
                AcDebug.Log($"Exception caught and logged in Program.reportAsync{Environment.NewLine}{ecx.Message}");
            }

            return(ret);
        }