Example #1
0
        static void Main()
        {
            string[] input = Console.ReadLine().Split(' ');
            //int nodes = int.Parse(input[0]);
            int streets = int.Parse(input[1]);
            //int hospitals = int.Parse(input[2]);

            string[] hospitalIds = Console.ReadLine().Split(' ');

            Dictionary<int, Node> allNodes = new Dictionary<int, Node>();

            for (int i = 0; i < streets; i++)
            {
                input = Console.ReadLine().Split(' ');
                int firstNodeId = int.Parse(input[0]);
                int secondNodeId = int.Parse(input[1]);
                int distance = int.Parse(input[2]);

                allNodes.AddSafe(firstNodeId, new Node(firstNodeId));
                allNodes.AddSafe(secondNodeId, new Node(secondNodeId));

                var firstNode = allNodes[firstNodeId];
                var secondNode = allNodes[secondNodeId];

                firstNode.Connections.Add(new Connection(secondNode, distance));
                secondNode.Connections.Add(new Connection(firstNode, distance));
            }

            int result = int.MaxValue;

            foreach (string id in hospitalIds)
            {
                var hospitalNode = allNodes[int.Parse(id)];
                hospitalNode.IsHospital = true;
            }

            foreach (string id in hospitalIds)
            {
                var startNode = allNodes[int.Parse(id)];
                Solve(allNodes, startNode);

                int tempSum = 0;
                foreach (var node in allNodes)
                {
                    if (!node.Value.IsHospital)
                    {
                        tempSum += node.Value.Distance;
                    }
                }

                if (tempSum < result)
                {
                    result = tempSum;
                }
            }

            Console.WriteLine(result);
        }
Example #2
0
        /// <summary>Returns a list of child processes of this process.</summary>
        /// <param name="process">The process to return the children of.</param>
        /// <param name="recursive">If true, all the children's children are included recursively. If false, only direct children are included.</param>
        public static List<int> ChildProcessIds(this Process process, bool recursive)
        {
            Dictionary<int, List<int>> tree = new Dictionary<int, List<int>>();
            foreach (var pair in ParentChildProcessIds())
                tree.AddSafe(pair.Item1, pair.Item2);

            if (!recursive)
            {
                if (tree.ContainsKey(process.Id))
                    return tree[process.Id];
                else
                    return new List<int>();
            }
            else
            {
                List<int> children = new List<int>();
                Queue<int> todo = new Queue<int>();
                todo.Enqueue(process.Id);
                while (todo.Count > 0)
                {
                    int id = todo.Dequeue();
                    if (tree.ContainsKey(id))
                        foreach (int child_id in tree[id])
                        {
                            children.Add(child_id);
                            todo.Enqueue(child_id);
                        }
                }
                return children;
            }
        }
Example #3
0
        static void Main()
        {
            #if DEBUG
            Console.SetIn(new System.IO.StreamReader("../../input.txt"));
            #endif
            Dictionary<int, Employer> employers = new Dictionary<int, Employer>();
            int n = int.Parse(Console.ReadLine());
            for (int i = 0; i < n; i++)
            {
                employers.AddSafe(i, new Employer(i));
                var employer = employers[i];

                string input = Console.ReadLine();
                for (int j = 0; j < n; j++)
                {
                    if (input[j] == 'Y')
                    {
                        employers.AddSafe(j, new Employer(j));
                        var worker = employers[j];

                        employer.Employers.Add(worker);
                    }
                }
            }

            long sum = 0;

            for (int i = 0; i < n; i++)
            {
                Solve(employers[i]);
            }

            foreach (var employer in employers.Values)
            {
                sum += employer.Salary;
            }

            Console.WriteLine(sum);
        }
Example #4
0
        static void Main()
        {
            #if DEBUG
            Console.SetIn(new System.IO.StreamReader("../../input.txt"));
            #endif

            Dictionary<int, Node> nodes = new Dictionary<int, Node>();
            int n = int.Parse(Console.ReadLine());
            for (int i = 0; i < n - 1; i++)
            {
                string[] input = Console.ReadLine().Split(new Char[] { '(', ')', '<', '-' }, StringSplitOptions.RemoveEmptyEntries);

                int parentId = int.Parse(input[0]);
                int childrenId = int.Parse(input[1]);

                Node parentNode = new Node(parentId);
                nodes.AddSafe(parentId, parentNode);
                parentNode = nodes[parentId];

                Node childrenNode = new Node(childrenId);
                nodes.AddSafe(childrenId, childrenNode);
                childrenNode = nodes[childrenId];

                parentNode.Childrens.Add(childrenNode);
                childrenNode.Childrens.Add(parentNode);
            }

            foreach (var value in nodes.Values)
            {
                if (value.Childrens.Count == 1)
                {
                    visitedIds.Clear();
                    DFS(value, 0);
                }
            }

            Console.WriteLine(maxSum);
        }
Example #5
0
        public static FncFile Parse(string sourceFile)
        {
            var sourceText = File.ReadAllText(sourceFile);
            var boxes = new List<Box>();

            // Turn into array of characters
            var lines = (sourceText.Replace("\r", "") + "\n\n").Split('\n');
            var source = new SourceAsChars(lines);

            if (lines.Length == 0)
                return new FncFile { Boxes = boxes, Source = source };

            var visited = new Dictionary<int, HashSet<int>>();

            // Find boxes
            for (int y = 0; y < source.NumLines; y++)
            {
                for (int x = 0; x < source[y].Length; x++)
                {
                    // Go looking for a box only if this is a top-left corner of a box
                    if (source.TopLine(x, y) != LineType.None || source.LeftLine(x, y) != LineType.None || source.RightLine(x, y) == LineType.None || source.BottomLine(x, y) == LineType.None)
                        continue;

                    if (visited.Contains(x, y))
                        continue;

                    // Find width of box by walking along top edge
                    var top = source.RightLine(x, y);
                    var index = x + 1;
                    while (index < source[y].Length && source.RightLine(index, y) == top)
                        index++;
                    if (index == source[y].Length || source.BottomLine(index, y) == LineType.None || source.TopLine(index, y) != LineType.None || source.RightLine(index, y) != LineType.None)
                        continue;
                    var width = index - x;

                    // Find height of box by walking along left edge
                    var left = source.BottomLine(x, y);
                    index = y + 1;
                    while (index < source.NumLines && source.BottomLine(x, index) == left)
                        index++;
                    if (index == source.NumLines || source.RightLine(x, index) == LineType.None || source.LeftLine(x, index) != LineType.None || source.BottomLine(x, index) != LineType.None)
                        continue;
                    var height = index - y;

                    // Verify the bottom edge
                    var bottom = source.RightLine(x, y + height);
                    index = x + 1;
                    while (index < source[y].Length && source.RightLine(index, y + height) == bottom)
                        index++;
                    if (index == source[y].Length || source.TopLine(index, y + height) == LineType.None || source.BottomLine(index, y + height) != LineType.None || source.RightLine(index, y + height) != LineType.None)
                        continue;
                    if (index - x != width)
                        continue;

                    // Verify the right edge
                    var right = source.BottomLine(x + width, y);
                    index = y + 1;
                    while (index < source.NumLines && source.BottomLine(x + width, index) == right)
                        index++;
                    if (index == source.NumLines || source.LeftLine(x + width, index) == LineType.None || source.RightLine(x + width, index) != LineType.None || source.BottomLine(x + width, index) != LineType.None)
                        continue;
                    if (index - y != height)
                        continue;

                    // If all edges are single lines, this is not a box
                    if (top == LineType.Single && right == LineType.Single && bottom == LineType.Single && left == LineType.Single)
                        continue;

                    for (int xx = 0; xx <= width; xx++)
                    {
                        visited.AddSafe(x + xx, y);
                        visited.AddSafe(x + xx, y + height);
                    }
                    for (int yy = 0; yy <= height; yy++)
                    {
                        visited.AddSafe(x, y + yy);
                        visited.AddSafe(x + width, y + yy);
                    }

                    boxes.Add(new Box
                    {
                        X = x,
                        Y = y,
                        Width = width,
                        Height = height,
                        LineTypes = Helpers.MakeDictionary(top, right, bottom, left)
                    });
                }
            }

            // Determine the location of text lines within every box
            foreach (var box in boxes)
            {
                var curTextLines = new HashSet<TextLine>();
                for (int by = 1; by < box.Height; by++)
                {
                    var y = box.Y + by;
                    TextLine curTextLine = null;
                    var curLineText = new StringBuilder();
                    for (int bx = 1; bx < box.Width; bx++)
                    {
                        var x = box.X + bx;

                        if (source.AnyLine(x, y))
                        {
                            if (curTextLine != null)
                            {
                                curTextLine.Content = curLineText.ToString();
                                curTextLines.Add(curTextLine);
                                curTextLine = null;
                                curLineText.Clear();
                            }
                        }
                        else
                        {
                            if (curTextLine == null)
                                curTextLine = new TextLine { X = x, Y = y };
                            curLineText.Append(source[y][x]);
                        }
                    }
                    if (curTextLine != null)
                    {
                        curTextLine.Content = curLineText.ToString();
                        curTextLines.Add(curTextLine);
                    }
                }

                // Group text lines by vertical adjacency
                var textAreas = new List<TextLine[]>();
                while (curTextLines.Count > 0)
                {
                    var first = curTextLines.First();
                    curTextLines.Remove(first);
                    var curGroup = new List<TextLine> { first };
                    while (true)
                    {
                        var next = curTextLines.FirstOrDefault(one => curGroup.Any(two => (one.Y == two.Y + 1 || one.Y == two.Y - 1) && one.X + one.Content.Length > two.X && one.X < two.X + two.Content.Length));
                        if (next == null)
                            break;
                        curGroup.Add(next);
                        curTextLines.Remove(next);
                    }
                    curGroup.Sort(CustomComparer<TextLine>.By(l => l.Y).ThenBy(l => l.X));
                    textAreas.Add(curGroup.ToArray());
                }
                box.TextAreas = textAreas.ToArray();
            }

            return new FncFile { Boxes = boxes, Source = source };
        }
Example #6
0
        static void Main()
        {
            #if DEBUG
            Console.SetIn(new System.IO.StreamReader("../../input.txt"));
            #endif
            Dictionary<string, House> houses = new Dictionary<string, House>();
            PriorityQueue<Connection> connections = new PriorityQueue<Connection>();
            List<Queue<House>> housePostion = new List<Queue<House>>();

            int n = int.Parse(Console.ReadLine());
            for (int i = 0; i < n; i++)
            {
                string[] input = Console.ReadLine().Split(' ');

                var house1 = new House(input[0]);
                var house2 = new House(input[1]);
                var distance = int.Parse(input[2]);

                houses.AddSafe(house1.Name, house1);
                houses.AddSafe(house2.Name, house2);

                Connection connection = new Connection()
                    {
                        House1 = houses[house1.Name],
                        House2 = houses[house2.Name],
                        Distance = distance
                    };
                connections.Enqueue(connection);
                houses[house1.Name].Connections.Add(connection);
                houses[house2.Name].Connections.Add(connection);

            }

            int index = 0;
            while (connections.Count > 0)
            {
                var connection = connections.Dequeue();
                if (connection.House1.Id == int.MaxValue)
                {
                    var queue = new Queue<House>();
                    if (connection.House2.Id == int.MaxValue)
                    {
                        connection.House1.Id = index;
                        connection.House2.Id = index;

                        queue.Enqueue(connection.House1);
                        queue.Enqueue(connection.House2);

                        housePostion.Add(queue);

                        index++;
                    }
                    else
                    {
                        connection.House1.Id = connection.House2.Id;
                        housePostion[connection.House2.Id].Enqueue(connection.House1);
                    }
                }
                else
                {
                    if (connection.House1.Id == connection.House2.Id)
                    {
                        connection.IsMinPath = false;
                        continue;
                    }
                    else
                    {
                        if (connection.House2.Id == int.MaxValue)
                        {
                            connection.House2.Id = connection.House1.Id;
                            housePostion[connection.House1.Id].Enqueue(connection.House2);
                        }
                        else
                        {
                            var queue = housePostion[connection.House2.Id];
                            while (queue.Count > 0)
                            {
                                var h = queue.Dequeue();
                                h.Id = connection.House1.Id;
                                housePostion[connection.House1.Id].Enqueue(h);
                            }
                        }
                    }
                }
            }
        }
        public void TestAddSafe()
        {
            // **
            // **  void AddSafe<K, V>(this IDictionary<K, List<V>> dic, K key, V value)
            // **
            {
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string>((IDictionary<string, List<string>>) null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string>((IDictionary<string, List<string>>) null, "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string>((IDictionary<string, HashSet<string>>) null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string>((IDictionary<string, HashSet<string>>) null, "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string>(new Dictionary<string, List<string>>(), null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string>(new Dictionary<string, HashSet<string>>(), null, null); });
                Assert.DoesNotThrow(() => { CollectionExtensions.AddSafe<string, string>(new Dictionary<string, List<string>>(), "", null); });
                Assert.DoesNotThrow(() => { CollectionExtensions.AddSafe<string, string>(new Dictionary<string, HashSet<string>>(), "", null); });

                var dicWithList = new Dictionary<string, List<GenericParameter>>();

                Assert.AreEqual(0, dicWithList.Count);
                Assert.Throws<KeyNotFoundException>(() => { var x = dicWithList["someKey"]; });
                dicWithList.AddSafe("someKey", new GenericParameter { SomeString = "someValue" });
                Assert.AreEqual(1, dicWithList.Count);
                var xList = dicWithList["someKey"];
                Assert.AreEqual(1, xList.Count);

                dicWithList.AddSafe("someKey", new GenericParameter { SomeString = "someOtherValue" });
                Assert.AreEqual(1, dicWithList.Count);
                Assert.AreEqual(2, xList.Count);

                Assert.IsTrue(xList.Select(g => g.SomeString).SequenceEqual(new string[] { "someValue", "someOtherValue" }));


                var dicWithHash = new Dictionary<string, HashSet<GenericParameter>>();

                Assert.AreEqual(0, dicWithHash.Count);
                Assert.Throws<KeyNotFoundException>(() => { var x = dicWithHash["someKey"]; });
                dicWithHash.AddSafe("someKey", new GenericParameter { SomeString = "someValue" });
                Assert.AreEqual(1, dicWithHash.Count);
                var xHash = dicWithHash["someKey"];
                Assert.AreEqual(1, xHash.Count);

                dicWithHash.AddSafe("someKey", new GenericParameter { SomeString = "someOtherValue" });
                Assert.AreEqual(1, dicWithHash.Count);
                Assert.AreEqual(2, xHash.Count);

                Assert.IsTrue(xHash.Select(g => g.SomeString).OrderBy(e => e).SequenceEqual(new string[] { "someOtherValue", "someValue" }));
            }

            // **
            // **  void AddSafe<K1, K2, V>(this IDictionary<K1, Dictionary<K2, V>> dic, K1 key1, K2 key2, V value)
            // **  void RemoveSafe<K1, K2, V>(this IDictionary<K1, Dictionary<K2, V>> dic, K1 key1, K2 key2)
            // **
            {
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(null, null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(null, "", null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(null, null, "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(null, "", "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), "", null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), null, "", null); });
                Assert.DoesNotThrow(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), "", "", null); });

                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(null, "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(null, null, ""); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(null, "", ""); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.RemoveSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), null, ""); });
                Assert.DoesNotThrow(() => { CollectionExtensions.RemoveSafe<string, string, string>(new Dictionary<string, Dictionary<string, string>>(), "", ""); });

                var dic = new Dictionary<string, Dictionary<string, GenericParameter>>();

                Assert.AreEqual(0, dic.Count);
                Assert.Throws<KeyNotFoundException>(() => { var x = dic["someKey"]; });

                dic.AddSafe("someKey", "someOtherKey", new GenericParameter { SomeString = "someValue" });
                Assert.AreEqual(1, dic.Count);
                var x2 = dic["someKey"];
                Assert.AreEqual(1, x2.Count);
                var x3 = dic["someKey"]["someOtherKey"];
                Assert.AreEqual("someValue", x3.SomeString);

                dic.AddSafe("someKey", "someOtherKey", new GenericParameter { SomeString = "someOtherValue" });
                Assert.AreEqual(1, dic.Count);
                Assert.AreEqual(1, x2.Count);
                Assert.AreEqual("someValue", x3.SomeString);

                var result = dic.RemoveSafe("nonExistentKey", "someOtherKey");
                Assert.IsFalse(result);
                Assert.AreEqual(1, dic.Count);
                result = dic.RemoveSafe("someKey", "nonExistentKey");
                Assert.IsFalse(result);
                Assert.AreEqual(1, dic.Count);
                result = dic.RemoveSafe("someKey", "someOtherKey");
                Assert.IsTrue(result);
                Assert.AreEqual(0, dic.Count);
            }

            // **
            // **  void AddSafe<K1, K2, V>(this IDictionary<K1, Dictionary<K2, List<V>>> dic, K1 key1, K2 key2, V value)
            // **
            {
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>((IDictionary<string, Dictionary<string, List<string>>>) null, null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>((IDictionary<string, Dictionary<string, List<string>>>) null, null, "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>((IDictionary<string, Dictionary<string, List<string>>>) null, "", null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>((IDictionary<string, Dictionary<string, List<string>>>) null, "", "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, List<string>>>(), null, null, null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, List<string>>>(), null, "", null); });
                Assert.Throws<ArgumentNullException>(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, List<string>>>(), "", null, null); });
                Assert.DoesNotThrow(() => { CollectionExtensions.AddSafe<string, string, string>(new Dictionary<string, Dictionary<string, List<string>>>(), "", "", null); });

                var dic = new Dictionary<string, Dictionary<string, List<GenericParameter>>>();

                Assert.AreEqual(0, dic.Count);
                Assert.Throws<KeyNotFoundException>(() => { var x = dic["someKey"]; });

                dic.AddSafe("someKey", "someOtherKey", new GenericParameter { SomeString = "someValue" });
                Assert.AreEqual(1, dic.Count);
                var x2 = dic["someKey"];
                Assert.AreEqual(1, x2.Count);
                var x3 = dic["someKey"]["someOtherKey"];
                Assert.AreEqual(1, x3.Count);

                dic.AddSafe("someKey", "someOtherKey", new GenericParameter { SomeString = "someOtherValue" });
                Assert.AreEqual(1, dic.Count);
                Assert.AreEqual(1, x2.Count);
                Assert.AreEqual(2, x3.Count);

                Assert.IsTrue(x3.Select(g => g.SomeString).SequenceEqual(new string[] { "someValue", "someOtherValue" }));
            }
        }
Example #8
0
        private static Dictionary<string, List<CsGenericTypeConstraint>> parseGenericTypeConstraints(TokenJar tok, ref int i)
        {
            var ret = new Dictionary<string, List<CsGenericTypeConstraint>>();
            while (tok[i].IsIdentifier("where"))
            {
                var startIndex = tok[i].StartIndex;
                i++;
                string genericParameter;
                try { genericParameter = tok[i].Identifier(); }
                catch (ParseException e) { throw new ParseException(e.Message, e.Index, ret, e); }
                if (ret.ContainsKey(genericParameter))
                    throw new ParseException("A constraint clause has already been specified for type parameter '{0}'. All of the constraints for a type parameter must be specified in a single where clause.".Fmt(genericParameter), tok[i].StartIndex, ret);
                i++;
                if (!tok[i].IsBuiltin(":"))
                    throw new ParseException("':' expected.", tok[i].StartIndex, ret);

                do
                {
                    i++;
                    if (tok[i].IsBuiltin("new") && tok.IndexExists(i + 2) && tok[i + 1].IsBuiltin("(") && tok[i + 2].IsBuiltin(")"))
                    {
                        i += 3;
                        ret.AddSafe(genericParameter, new CsGenericTypeConstraintNew { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex });
                    }
                    else if (tok[i].IsBuiltin("class"))
                    {
                        ret.AddSafe(genericParameter, new CsGenericTypeConstraintClass { StartIndex = startIndex, EndIndex = tok[i].EndIndex });
                        i++;
                    }
                    else if (tok[i].IsBuiltin("struct"))
                    {
                        ret.AddSafe(genericParameter, new CsGenericTypeConstraintStruct { StartIndex = startIndex, EndIndex = tok[i].EndIndex });
                        i++;
                    }
                    else if (tok[i].Type != TokenType.Identifier)
                        throw new ParseException("Generic type constraint ('new()', 'class', 'struct', or type identifier) expected.", tok[i].StartIndex, ret);
                    else
                    {
                        try
                        {
                            var typeName = parseTypeName(tok, ref i, typeIdentifierFlags.AllowKeywords).Item1;
                            ret.AddSafe(genericParameter, new CsGenericTypeConstraintBaseClass { StartIndex = startIndex, EndIndex = tok[i - 1].EndIndex, BaseClass = typeName });
                        }
                        catch (ParseException e)
                        {
                            if (e.IncompleteResult is CsTypeName)
                                ret.AddSafe(genericParameter, new CsGenericTypeConstraintBaseClass { StartIndex = tok[i].StartIndex, BaseClass = (CsTypeName) e.IncompleteResult });
                            throw new ParseException(e.Message, e.Index, ret, e);
                        }
                    }
                }
                while (tok[i].IsBuiltin(","));
            }
            return ret;
        }
Example #9
0
        public override ExecutionEnvironment Compile(string source, string input)
        {
            var lines = source.Replace("\r", "").Split('\n');
            if (lines.Length == 0)
                throw new CompileException("Program does not contain any arrows.", 0, 0);

            var width = lines.Max(line => line.Length);
            var height = lines.Length;
            var chars = lines.Select(line => line + new string(' ', width - line.Length)).ToArray();

            var forEachArrow = Ut.Lambda((Action<int, int, int, bool, int, char> action) =>
            {
                int x = 0, y = 0;
                for (int i = 0; i < source.Length; i++)
                {
                    var ch = source[i];
                    if (ch == '\r')
                        continue;
                    else if (ch == '\n')
                    {
                        y++;
                        x = 0;
                        continue;
                    }
                    if (chars[y][x] != ch)
                        throw new CompileException("Internal parse error: This is a bug in the parser.", i, 1);
                    if (ch != ' ')
                    {
                        var p = "↖↑↗→↘↓↙←".IndexOf(ch);
                        var single = true;
                        if (p == -1)
                        {
                            p = "⤡↕⤢↔".IndexOf(ch);
                            single = false;
                        }
                        if (p == -1)
                            throw new CompileException("Invalid character: “{0}”.".Fmt(ch), i, 1);
                        action(x, y, i, single, p, ch);
                    }
                    x++;
                }
            });

            var followArrow = Ut.Lambda((int ax, int ay, int dx, int dy, Action<int, int> found) =>
            {
                while (true)
                {
                    ax += dx;
                    ay += dy;
                    if (ax < 0 || ax >= width || ay < 0 || ay >= height)
                        return false;
                    if (chars[ay][ax] != ' ')
                    {
                        found(ax, ay);
                        return true;
                    }
                }
            });

            // STEP 1: Determine, for each arrow, which incoming directions have other arrows point at it
            var pointedToBy = new Dictionary<int, Dictionary<int, List<Tuple<int, int, int>>>>();
            forEachArrow((x, y, i, single, p, ch) =>
            {
                for (int dd = 0; dd < 8; dd++)
                    followArrow(x, y, -_dx[dd], -_dy[dd], (fx, fy) =>
                    {
                        if (_darr[dd].Contains(chars[fy][fx]))
                            pointedToBy.AddSafe(x, y, Tuple.Create(fx, fy, (p + 8 - dd) % 8));
                    });
            });

            // STEP 2: Translate each arrow into a Node instance with the appropriate Instruction
            var nodes = Ut.NewArray<Node>(height, width);
            forEachArrow((x, y, i, single, p, ch) =>
            {
                var pointedToFrom = (pointedToBy.ContainsKey(x) && pointedToBy[x].ContainsKey(y) ? pointedToBy[x][y].Select(tup => tup.Item3) : Enumerable.Empty<int>()).Order().ToList();
                nodes[y][x] = new Node(x, y, i,
                    single ?
                        pointedToFrom.SequenceEqual(_zero) ? Instruction.Zero :
                        pointedToFrom.SequenceEqual(_stdin) ? Instruction.Stdin :
                        pointedToFrom.SequenceEqual(_concat) ? Instruction.Concat :
                        pointedToFrom.SequenceEqual(_inverse) ? Instruction.Inverse :
                        pointedToFrom.SequenceEqual(_noop) ? Instruction.NoOp :
                        pointedToFrom.SequenceEqual(_label) ? Instruction.Label :
                        Ut.Throw<Instruction>(new CompileException("Invalid combination of arrows pointing at arrow.", i, 1))
                    : // double arrow
                        pointedToFrom.SequenceEqual(_splitter[0]) || pointedToFrom.SequenceEqual(_splitter[1]) ? Instruction.Splitter :
                        pointedToFrom.SequenceEqual(_isZero[0]) || pointedToFrom.SequenceEqual(_isZero[1]) ? Instruction.IsZero :
                        pointedToFrom.SequenceEqual(_isEmpty[0]) || pointedToFrom.SequenceEqual(_isEmpty[1]) ? Instruction.IsEmpty :
                        Ut.Throw<Instruction>(new CompileException("Invalid combination of arrows pointing at arrow.", i, 1)));
            });

            // STEP 3: For each node, determine which other nodes it is pointing to and are pointing to it
            forEachArrow((x, y, i, single, p, ch) =>
            {
                var pointedToFrom = (pointedToBy.ContainsKey(x) && pointedToBy[x].ContainsKey(y) ? pointedToBy[x][y] : Enumerable.Empty<Tuple<int, int, int>>()).OrderBy(tup => tup.Item3).ToList();
                if (single)
                {
                    if (!followArrow(x, y, _dx[p], _dy[p], (fx, fy) => { nodes[y][x].PointsTo.Add(nodes[fy][fx]); }))
                        nodes[y][x].PointsTo.Add(null);
                }
                else
                {
                    if (!followArrow(x, y, _dx[p], _dy[p], (fx, fy) => { nodes[y][x].PointsTo.Add(nodes[fy][fx]); }))
                        nodes[y][x].PointsTo.Add(null);
                    if (!followArrow(x, y, -_dx[p], -_dy[p], (fx, fy) => { nodes[y][x].PointsTo.Add(nodes[fy][fx]); }))
                        nodes[y][x].PointsTo.Add(null);
                    var pointedToFrom2 = pointedToFrom.Select(tup => tup.Item3);
                    if (pointedToFrom2.SequenceEqual(_isZero[1]) || pointedToFrom2.SequenceEqual(_isEmpty[1]))
                        nodes[y][x].PointsTo.Reverse();
                }
                nodes[y][x].PointedToBy.AddRange(pointedToFrom.Select(tup => nodes[tup.Item2][tup.Item1]));
            });

            var nodesArr = nodes.SelectMany(row => row.Where(n => n != null)).ToArray();
            return new ZiimEnv
            {
                Nodes = nodesArr,
                Threads = nodesArr.Where(n => n.Instruction == Instruction.Zero).Select(n => new Thread { CurrentValue = Bits.Zero, CurrentInstruction = n, Suspended = false }).ToList(),
                Input = input.ToUtf8().SelectMany(b => Enumerable.Range(0, 8).Select(i => (b & (1 << (7 - i))) != 0)).ToQueue()
            };
        }