#!/usr/bin/ruby

func m_exec(lz, *args) {

    lz.kind_of(LazyMethod) &&
        return lz(args...);

    die "[ERROR] Argument `#{lz}' is not a LazyMethod"
}

# Create some simple lazy-methods
var lc = "TEST".method(:lc);
var cos = Number.method(:cos);

# Create some complex lazy-methods
var lz1 = "h€llo".method(:ucfirst).method(:concat, '!').method(:say);
var lz2 = "World".method(:print)
var lz3 = "".method(:concat, '!').method(:say);

# Test the simple lazy-methods
lc()          == "test"          || "error lc()".die;
lc.index('e') == 1               || "error .index()".die;
cos(5)        == cos(5)          || "Error cos()".die;

# More tests
var lz4 = String.method(:uc, 'abc')
assert_eq(m_exec(lz4), 'ABC')
assert_eq(m_exec(String.method(:uc), 'abc'), 'ABC')

var lz5 = 'abc'.method(:uc).method(:reverse).method(:chars)
assert_eq(m_exec(lz5), ['C', 'B', 'A'])

var lz6 = String.method(:uc, 'abc').method(:reverse)
assert_eq(m_exec(lz6), 'CBA')
assert_eq(m_exec(lz6), 'CBA')
assert_eq(lz6.split('B'), ['C', 'A'])

var lz7 = 'abc'.method(:uc).method(:reverse)
assert_eq(m_exec(lz7), 'CBA')
assert_eq(lz7.split('B'), ['C', 'A'])

var lz8 = ' '.method(:join, 'a', 'b')
assert_eq(m_exec(lz8), 'a b')
assert_eq(m_exec(lz8, 'c'), 'a b c')
assert_eq(m_exec(lz8.method(:uc).method(:split, ' ')), ['A', 'B'])
assert_eq(m_exec(lz8.method(:uc).method(:split), ' '), ['A', 'B'])

var lz8 = String.method(:join, ' ', 'a', 'b')
assert_eq(m_exec(lz8), 'a b')
assert_eq(m_exec(lz8, 'c'), 'a b c')
assert_eq(m_exec(lz8.method(:uc).method(:split, ' ')), ['A', 'B'])
assert_eq(m_exec(lz8.method(:uc).method(:split), ' '), ['A', 'B'])

var lzsplit = "a-b-c".method(:uc).method(:split)
assert_eq(lzsplit(''), ["A", "-", "B", "-", "C"])
assert_eq(lzsplit('-'), ["A", "B", "C"])

say "----BEGIN----";

# Display "H€llo!\nWorld!"
m_exec(lz1);
m_exec(lz2);
m_exec(lz3);

say "-----END-----";

# Test the complex lazy-methods
{lz1()}.capture.chomp == "H€llo!" || "lz1 error".die;
{lz2()}.capture.chomp == "World"  || "lz2 error".die;
{lz3()}.capture.chomp == "!"      || "lz3 error".die;

assert_eq(Number.method(:isqrt)(42), 6)
assert_eq(Number.methods(){:isqrt}(42), 6)
assert_eq(Number.methods(42){:isqrt}(), 6)

assert_eq(Number.methods(){:add}(4, 5), 9)
assert_eq(Number.methods(4){:add}(5), 9)
assert_eq(Number.methods(4, 5){:add}(), 9)

assert_eq(Number.method(:add)(4, 5), 9)
assert_eq(Number.method(:add, 4)(5), 9)
assert_eq(Number.method(:add, 4, 5)(), 9)

assert_eq(4.method(:add)(5), 9)
assert_eq(4.method(:add, 5)(), 9)

do {
    var t = Number.methods(4, 5)

    assert_eq(t{:add}(), 9)
    assert_eq(t{:mul}(), 20)
    assert_eq(t{:sub}(), -1)
}

do {
    var add1 = 1.method(:add)

    assert_eq(add1(41), 42)
    assert_eq([1,2,3].map { add1(_) }, [2,3,4])
    assert_eq([1,2,3].map(add1), [2,3,4])
}