private TiState Step(TiState state)
        {
            var headAddr = state.Stack.Peek();
            var headNode = state.Heap[headAddr];

            return(headNode switch
            {
                TiNode.Number number => StepNum(state, number),
                TiNode.Application application => StepAp(state, application),
                TiNode.Supercombinator supercombinator => StepSc(state, supercombinator),
                TiNode.Indirection indirection => StepInd(state, indirection),
                TiNode.Primitive primitive => StepPrim(state, primitive),
                TiNode.Data data => StepData(state, data),

                _ => throw new ArgumentOutOfRangeException(nameof(headNode))
            });
        private static (ImmutableDictionary <int, TiNode>, int) MarkFrom(ImmutableDictionary <int, TiNode> heap, int addr)
        {
            var node = heap[addr];

            switch (node)
            {
            case TiNode.Application application:
                int function, argument;
                (heap, function) = MarkFrom(heap, application.Function);
                (heap, argument) = MarkFrom(heap, application.Argument);

                node = new TiNode.Application(function, argument);
                break;

            case TiNode.Data data:
                var components = new List <int>();
                foreach (var component in data.Components)
                {
                    int newComponent;
                    (heap, newComponent) = MarkFrom(heap, component);
                    components.Add(newComponent);
                }

                node = new TiNode.Data(data.Tag, components);
                break;

            case TiNode.Indirection indirection: return(MarkFrom(heap, indirection.Address));

            case TiNode.Number _: break;

            case TiNode.Primitive _: break;

            case TiNode.Supercombinator _: break;

            case Marked _: return(heap, addr);

            default: throw new ArgumentOutOfRangeException(nameof(node));
            }

            heap = heap.SetItem(addr, new Marked(node));
            return(heap, addr);
        }