' String manipulation and macro utilities
#COMPILE EXE
#DIM ALL

MACRO PrintLabel(label, value)
    PRINT label; " = "; value
END MACRO

MACRO OpAdd(a, b) = (a) + (b)
MACRO OpSub(a, b) = (a) - (b)
MACRO OpMul(a, b) = (a) * (b)
MACRO OpDiv(a, b) = (a) / (b)
MACRO OpMod(a, b) = (a) MOD (b)

MACRO IsNeg(x) = (x) < 0
MACRO IsPos(x) = (x) > 0
MACRO IsZero(x) = (x) = 0

MACRO MinOf(a, b) = IIF((a) < (b), (a), (b))
MACRO MaxOf(a, b) = IIF((a) > (b), (a), (b))

MACRO Between(a, lo, hi) = (a) >= (lo) AND (a) <= (hi)
MACRO Clamp(x, lo, hi) = MinOf(MaxOf(x, lo), hi)

MACRO Square(x) = (x) * (x)
MACRO Cube(x) = (x) * (x) * (x)

MACRO Even(x) = (x) MOD 2 = 0
MACRO Odd(x) = (x) MOD 2 <> 0

SUB TestOperators()
    PRINT "--- Operator macros ---"
    LOCAL a  AS LONG
    LOCAL b  AS LONG
    a = 15
    b = 4
    PrintLabel("Add", OpAdd(a, b))
    PrintLabel("Sub", OpSub(a, b))
    PrintLabel("Mul", OpMul(a, b))
    PrintLabel("Div", OpDiv(a, b))
    PrintLabel("Mod", OpMod(a, b))
END SUB

SUB TestSign()
    PRINT "--- Sign tests ---"
    PrintLabel("IsNeg(-5)", IsNeg(-5))
    PrintLabel("IsNeg(3)", IsNeg(3))
    PrintLabel("IsPos(7)", IsPos(7))
    PrintLabel("IsPos(-2)", IsPos(-2))
    PrintLabel("IsZero(0)", IsZero(0))
END SUB

SUB TestMinMax()
    PRINT "--- MIN/MAX tests ---"
    PrintLabel("MinOf(10,20)", MinOf(10, 20))
    PrintLabel("MinOf(50,5)", MinOf(50, 5))
    PrintLabel("MaxOf(10,20)", MaxOf(10, 20))
    PrintLabel("MaxOf(50,5)", MaxOf(50, 5))
END SUB

SUB TestBounds()
    PRINT "--- Bounds tests ---"
    PrintLabel("Between(5,0,10)", Between(5, 0, 10))
    PrintLabel("Between(15,0,10)", Between(15, 0, 10))
    PrintLabel("Clamp(-5,0,10)", Clamp(-5, 0, 10))
    PrintLabel("Clamp(15,0,10)", Clamp(15, 0, 10))
END SUB

SUB TestMath()
    PRINT "--- Math tests ---"
    PrintLabel("Square(7)", Square(7))
    PrintLabel("Cube(4)", Cube(4))
    PrintLabel("Even(10)", Even(10))
    PrintLabel("Even(7)", Even(7))
    PrintLabel("Odd(10)", Odd(10))
    PrintLabel("Odd(7)", Odd(7))
END SUB

FUNCTION Factorial(BYVAL n AS LONG) AS LONG
    IF n <= 1 THEN
        FUNCTION = 1
    ELSE
        FUNCTION = n * Factorial(n - 1)
    END IF
END FUNCTION

FUNCTION GCD(BYVAL a AS LONG, BYVAL b AS LONG) AS LONG
    LOCAL t AS LONG
    WHILE b <> 0
        t = b
        b = a MOD b
        a = t
    WEND
    FUNCTION = a
END FUNCTION

FUNCTION LCM(BYVAL a AS LONG, BYVAL b AS LONG) AS LONG
    FUNCTION = a * b / GCD(a, b)
END FUNCTION

FUNCTION IsPrime(BYVAL n AS LONG) AS LONG
    LOCAL i AS LONG
    IF n < 2 THEN
        FUNCTION = 0
    END IF
    FOR i = 2 TO SQR(n)
        IF n MOD i = 0 THEN
            FUNCTION = 0
        END IF
    NEXT i
    FUNCTION = 1
END FUNCTION

SUB TestFunctions()
    PRINT "--- Function tests ---"
    LOCAL i AS LONG
    FOR i = 0 TO 5
        PrintLabel("Factorial(" + STR$(i) + ")", Factorial(i))
    NEXT i
    PrintLabel("GCD(12,18)", GCD(12, 18))
    PrintLabel("LCM(6,8)", LCM(6, 8))
    PrintLabel("IsPrime(17)", IsPrime(17))
    PrintLabel("IsPrime(21)", IsPrime(21))
END SUB

SUB TestArrayOps()
    PRINT "--- Array operations ---"
    DIM d(0:4) AS LONG
    LOCAL i  AS LONG
    LOCAL s  AS LONG
    LOCAL p  AS LONG
    FOR i = 0 TO 4
        d(i) = i * 10
    NEXT i
    s = 0
    p = 1
    FOR i = 0 TO 4
        s = OpAdd(s, d(i))
        IF i > 0 THEN p = OpMul(p, d(i))
    NEXT i
    PrintLabel("Sum", s)
    PrintLabel("Product", p)
    PrintLabel("d(0)", d(0))
    PrintLabel("d(4)", d(4))
END SUB

FUNCTION PBMAIN() AS LONG
    PRINT "=== Stringify and Macro Demo ==="
    TestOperators()
    TestSign()
    TestMinMax()
    TestBounds()
    TestMath()
    TestFunctions()
    TestArrayOps()
    PrintLabel("MaxOf(MinOf(50,100),25)", MaxOf(MinOf(50, 100), 25))
    PrintLabel("Clamp(200,0,100)", Clamp(200, 0, 100))
    PRINT "=== End Stringify Demo ==="
    FUNCTION = 0
END FUNCTION
