// File: my_heap.zig
// Created Time: 2023-01-14
// Author: sjinzh (sjinzh@gmail.com)

const std = @import("std");
const inc = @import("include");

// 堆类简易实现
pub fn MaxHeap(comptime T: type) type {
    return struct {
        const Self = @This();
        
        maxHeap: ?std.ArrayList(T) = null,      // 使用列表而非数组，这样无需考虑扩容问题

        // 构造函数，根据输入列表建堆
        pub fn init(self: *Self, allocator: std.mem.Allocator, nums: []const T) !void {
            if (self.maxHeap != null) return;
            self.maxHeap = std.ArrayList(T).init(allocator);
            // 将列表元素原封不动添加进堆
            try self.maxHeap.?.appendSlice(nums);
            // 堆化除叶结点以外的其他所有结点
            var i: usize = parent(self.size() - 1) + 1;
            while (i > 0) : (i -= 1) {
                try self.siftDown(i - 1);
            }
        }

        // 析构函数，释放内存
        pub fn deinit(self: *Self) void {
            if (self.maxHeap != null) self.maxHeap.?.deinit();
        }

        // 获取左子结点索引
        fn left(i: usize) usize {
            return 2 * i + 1;
        }

        // 获取右子结点索引
        fn right(i: usize) usize {
            return 2 * i + 2;
        }

        // 获取父结点索引
        fn parent(i: usize) usize {
            // return (i - 1) / 2; // 向下整除
            return @divFloor(i - 1, 2);
        }

        // 交换元素
        fn swap(self: *Self, i: usize, j: usize) !void {
            var a = self.maxHeap.?.items[i];
            var b = self.maxHeap.?.items[j];
            var tmp = a;
            try self.maxHeap.?.replaceRange(i, 1, &[_]T{b});
            try self.maxHeap.?.replaceRange(j, 1, &[_]T{tmp});
        }

        // 获取堆大小
        pub fn size(self: *Self) usize {
            return self.maxHeap.?.items.len;
        }

        // 判断堆是否为空
        pub fn isEmpty(self: *Self) bool {
            return self.size() == 0;
        }

        // 访问堆顶元素
        pub fn peek(self: *Self) T {
            return self.maxHeap.?.items[0];
        }  

        // 元素入堆
        pub fn push(self: *Self, val: T) !void {
            // 添加结点
            try self.maxHeap.?.append(val);
            // 从底至顶堆化
            try self.siftUp(self.size() - 1);
        }  

        // 从结点 i 开始，从底至顶堆化
        fn siftUp(self: *Self, i_: usize) !void {
            var i = i_;
            while (true) {
                // 获取结点 i 的父结点
                var p = parent(i);
                // 当“越过根结点”或“结点无需修复”时，结束堆化
                if (p < 0 or self.maxHeap.?.items[i] <= self.maxHeap.?.items[p]) break;
                // 交换两结点
                try self.swap(i, p);
                // 循环向上堆化
                i = p;
            }
        }

        // 元素出堆
        pub fn poll(self: *Self) !T {
            // 判断处理
            if (self.isEmpty()) unreachable;
            // 交换根结点与最右叶结点（即交换首元素与尾元素）
            try self.swap(0, self.size() - 1);
            // 删除结点
            var val = self.maxHeap.?.pop();
            // 从顶至底堆化
            try self.siftDown(0);
            // 返回堆顶元素
            return val;
        } 

        // 从结点 i 开始，从顶至底堆化
        fn siftDown(self: *Self, i_: usize) !void {
            var i = i_;
            while (true) {
                // 判断结点 i, l, r 中值最大的结点，记为 ma
                var l = left(i);
                var r = right(i);
                var ma = i;
                if (l < self.size() and self.maxHeap.?.items[l] > self.maxHeap.?.items[ma]) ma = l;
                if (r < self.size() and self.maxHeap.?.items[r] > self.maxHeap.?.items[ma]) ma = r;
                // 若结点 i 最大或索引 l, r 越界，则无需继续堆化，跳出
                if (ma == i) break;
                // 交换两结点
                try self.swap(i, ma);
                // 循环向下堆化
                i = ma;
            }
        }

        fn lessThan(context: void, a: T, b: T) std.math.Order {
            _ = context;
            return std.math.order(a, b);
        }

        fn greaterThan(context: void, a: T, b: T) std.math.Order {
            return lessThan(context, a, b).invert();
        }

        // 打印堆（二叉树）
        pub fn print(self: *Self, mem_allocator: std.mem.Allocator) !void {
            const PQgt = std.PriorityQueue(T, void, greaterThan);
            var queue = PQgt.init(std.heap.page_allocator, {});
            defer queue.deinit();
            try queue.addSlice(self.maxHeap.?.items);
            try inc.PrintUtil.printHeap(T, mem_allocator, queue);
        } 
    };
}

// Driver Code
pub fn main() !void {
    // 初始化内存分配器
    var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer mem_arena.deinit();
    const mem_allocator = mem_arena.allocator();

    // 初始化大顶堆
    var maxHeap = MaxHeap(i32){};
    try maxHeap.init(std.heap.page_allocator, &[_]i32{ 9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2 });
    defer maxHeap.deinit();
    std.debug.print("\n输入列表并建堆后\n", .{});
    try maxHeap.print(mem_allocator);

    // 获取堆顶元素
    var peek = maxHeap.peek();
    std.debug.print("\n堆顶元素为 {}\n", .{peek});

    // 元素入堆
    const val = 7;
    try maxHeap.push(val);
    std.debug.print("\n元素 {} 入堆后\n", .{val});
    try maxHeap.print(mem_allocator);

    // 堆顶元素出堆
    peek = try maxHeap.poll();
    std.debug.print("\n堆顶元素 {} 出堆后\n", .{peek});
    try maxHeap.print(mem_allocator);

    // 获取堆的大小
    var size = maxHeap.size();
    std.debug.print("\n堆元素数量为 {}", .{size});

    // 判断堆是否为空
    var is_empty = maxHeap.isEmpty();
    std.debug.print("\n堆是否为空 {}\n", .{is_empty});

    _ = try std.io.getStdIn().reader().readByte();
}

