const std = @import("std");

const example = @embedFile("example");
const input = @embedFile("input");

var allocator: std.mem.Allocator = undefined;

pub fn main() anyerror!void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    allocator = arena.allocator();

    try std.testing.expectEqual(try solve(example), 140);
    const result = try solve(input);
    try std.io.getStdOut().writer().print("{}\n", .{result});
}

const ListErrors = error{
    OutOfMemory,
};

const List = struct {
    data: std.ArrayList(Elt),
    fn addOne(l: *List) !*Elt {
        return try l.data.addOne();
    }
    fn init(reader: anytype) !*List {
        var l = try allocator.create(List);
        l.data = std.ArrayList(Elt).init(allocator);
        while (true) {
            const c = reader.readByte() catch break;
            switch (c) {
                '[' => {
                    var e = try l.data.addOne();
                    e.* = Elt{ .list = try List.init(reader) };
                },
                ']' => {
                    return l;
                },
                ',' => { // comma after a list ending, ignore
                },
                else => { // a digit
                    var e = try l.data.addOne();
                    e.* = Elt{ .int = c - '0' };
                    while (true) {
                        const d = reader.readByte() catch unreachable;
                        if (d == ']') {
                            return l;
                        } else if (d == ',') {
                            break;
                        }
                        e.int = e.int * 10 + d;
                    }
                },
            }
        }
        return l;
    }
    fn initEmpty() !*List {
        var l = try allocator.create(List);
        l.data = std.ArrayList(Elt).init(allocator);
        return l;
    }
    fn initSingleton(n: u8) !*List {
        var l = try allocator.create(List);
        l.data = std.ArrayList(Elt).init(allocator);
        var e = try l.data.addOne();
        e.* = Elt{ .int = n };
        return l;
    }
    fn isLowerThan(l: *List, o: *List) ListErrors!?bool {
        var i: usize = 0;
        while (true) : (i += 1) {
            if (o.len() <= i) {
                if (l.len() <= i) {
                    return null;
                }
                return false;
            }
            if (l.len() <= i) {
                return true;
            }
            const t = try l.data.items[i].isLowerThan(&(o.data.items[i]));
            if (t != null) {
                return t;
            }
        }
    }
    inline fn len(l: *List) usize {
        return l.data.items.len;
    }
    fn print(l: *List) void {
        for (l.data.items) |e| {
            switch (e) {
                Elt.int => |i| std.debug.print("{d} ", .{i}),
                Elt.list => |o| {
                    std.debug.print("[", .{});
                    o.print();
                    std.debug.print("]", .{});
                },
            }
        }
    }
};

const EltType = enum {
    int,
    list,
};

const Elt = union(EltType) {
    int: u8,
    list: *List,
    fn isLowerThan(e: *Elt, f: *Elt) !?bool {
        switch (e.*) {
            Elt.int => |i| switch (f.*) {
                Elt.int => |j| {
                    if (i == j) {
                        return null;
                    }
                    return i < j;
                },
                Elt.list => |o| {
                    var l = try List.initSingleton(i);
                    return l.isLowerThan(o);
                },
            },
            Elt.list => |l| switch (f.*) {
                Elt.int => |j| {
                    var ll = Elt{ .list = try List.initSingleton(j) };
                    return e.isLowerThan(&ll);
                },
                Elt.list => |o| {
                    return l.isLowerThan(o);
                },
            },
        }
    }
};

fn lesserThan(context: void, a: *Elt, b: *Elt) bool {
    _ = context;
    var tt = a.isLowerThan(b) catch unreachable;
    if (tt) |t| {
        if (t) {
            return true;
        }
    }
    return false;
}

fn solve(puzzle: []const u8) !u64 {
    var it = std.mem.tokenize(u8, puzzle, "\n");
    var list = std.ArrayList(*Elt).init(allocator);
    var divider2Line = std.io.fixedBufferStream("[[2]]");
    var div2 = try list.addOne();
    div2.* = try allocator.create(Elt);
    div2.*.* = Elt{ .list = try List.init(divider2Line.reader()) };
    var divider6Line = std.io.fixedBufferStream("[[6]]");
    var div6 = try list.addOne();
    div6.* = try allocator.create(Elt);
    div6.*.* = Elt{ .list = try List.init(divider6Line.reader()) };
    // process input
    while (it.next()) |line| {
        var bs = std.io.fixedBufferStream(line);
        var elt = try list.addOne();
        elt.* = try allocator.create(Elt);
        elt.*.* = Elt{ .list = try List.init(bs.reader()) };
    }
    // sort the list
    std.sort.sort(*Elt, list.items, {}, lesserThan);
    //for (list.items) |l| {
    //    l.list.print();
    //    std.debug.print("\n", .{});
    //}
    // compute output
    var ret: usize = 1;
    for (list.items) |l, i| {
        switch (l.*) {
            Elt.int => {},
            Elt.list => |ll| {
                if (ll.len() != 1) {
                    continue;
                }
                switch (ll.data.items[0]) {
                    Elt.int => {},
                    Elt.list => |lll| {
                        if (lll.len() != 1) {
                            continue;
                        }
                        switch (lll.data.items[0]) {
                            Elt.int => {},
                            Elt.list => |llll| {
                                if (llll.len() != 1) {
                                    continue;
                                }
                                switch (llll.data.items[0]) {
                                    Elt.int => |n| {
                                        if (n == 2 or n == 6) {
                                            ret *= (i + 1);
                                        }
                                    },
                                    Elt.list => {},
                                }
                            },
                        }
                    },
                }
            },
        }
    }
    return ret;
}