const std = @import("std");
const print = std.debug.print;
const expect = std.testing.expect;

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

test "simple partition #1" {
    const arr = try allocator.alloc(u64, 3);
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 5;
    defer {
        allocator.free(arr);
    }
    const res = bipartition(arr, 0, @as(u64, arr.len - 1), 2);
    // std.log.warn("Res {}", .{res});
    try expect(res == 1);
}

test "simple partition #2" {
    const arr = try allocator.alloc(u64, 4);
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 5;
    arr[3] = 6;
    defer {
        allocator.free(arr);
    }
    const res = bipartition(arr, 0, @as(u64, arr.len - 1), 4);
    // std.log.warn("Res {}", .{res});
    try expect(res == 2);
}

test "simple partition #3" {
    const arr = try allocator.alloc(u64, 4);
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 5;
    arr[3] = 6;
    defer {
        allocator.free(arr);
    }
    const res = bipartition(arr, 0, @as(u64, arr.len - 1), 0);
    // std.log.warn("Res {}", .{res});
    try expect(res == 0);
}

test "simple partition #4" {
    const arr = try allocator.alloc(u64, 4);
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 5;
    arr[3] = 6;
    defer {
        allocator.free(arr);
    }
    const res = bipartition(arr, 0, @as(u64, arr.len - 1), 7);
    // std.log.warn("Res {}", .{res});
    try expect(res == 4);
}

test "simple partition #5" {
    const arr = try allocator.alloc(u64, 4);
    arr[0] = 1;
    arr[1] = 3;
    arr[2] = 5;
    arr[3] = 6;
    defer {
        allocator.free(arr);
    }
    const res = bipartition(arr, 0, @as(u64, arr.len - 1), 6);
    // std.log.warn("Res {}", .{res});
    try expect(res == 3);
}

pub fn bipartition(arr: []u64, t1: u64, t2: u64, v: u64) u64 {
    // std.log.warn("Arr called with {}, {}, {}", .{ t1, t2, v });
    var x1: u64 = t1;
    var x2: u64 = t2;
    var x: u64 = undefined;

    if (arr.len <= 0) {
        return x1;
    }

    if (v < arr[x1]) {
        return x1;
    }

    if (v > arr[t2]) {
        return t2 + 1;
    }

    while (x1 < x2 - 1) {
        // std.log.warn("Round {},{}", .{ t1, t2 });
        x = (x1 + x2) / 2;
        if (arr[x] < v) {
            x1 = x;
        } else {
            x2 = x;
        }
    }

    return x2;
}

const BTree = struct {
    n: u64,
    values: []u64,
    children: []?BTree,
};

pub fn BTree_Create(n: u64) !*BTree {
    var q = try allocator.create(BTree);
    q.values = try allocator.alloc(u64, n);
    q.children = try allocator.alloc(?BTree, n + 1);
    return q;
}

pub fn BTree_Destroy(q: *BTree) !void {
    allocator.free(q.values);
    allocator.free(q.children);
    allocator.destroy(q);
}

pub fn BTree_Add(q: *BTree, v: u64) *BTree {
    if (q.n <= q.values.len) {
        const idx = bipartition(q.values, 0, q.n, v);
        std.log.warn("Idx {}", idx);
        const i: u64 = q.n;
        while (i >= idx) {
            q.values[idx + 1] = q.values[idx];
            // TODO add here moving children pointers
            idx -= 1;
        }
        q.n += 1;
        q.values[idx] = v;
        // TODO insert here element
    }
    return q;
}

pub fn main() !void {
    var q = try BTree_Create(4);
    q.n = 1;
    print("Hello World {}!!!\n", .{2});

    try BTree_Destroy(q);

    defer {
        _ = gpa.deinit();
    }
}
