"""
Core Primitives - Always available, minimal essential set.

These are the primitives that form the foundation of the language.
They cannot be overridden by libraries.
"""


# Arithmetic
def prim_add(*args):
    if len(args) == 0:
        return 0
    result = args[0]
    for arg in args[1:]:
        result = result + arg
    return result


def prim_sub(a, b=None):
    if b is None:
        return -a
    return a - b


def prim_mul(*args):
    if len(args) == 0:
        return 1
    result = args[0]
    for arg in args[1:]:
        result = result * arg
    return result


def prim_div(a, b):
    return a / b


def prim_mod(a, b):
    return a % b


def prim_abs(x):
    return abs(x)


def prim_min(*args):
    return min(args)


def prim_max(*args):
    return max(args)


def prim_round(x):
    return round(x)


def prim_floor(x):
    import math
    return math.floor(x)


def prim_ceil(x):
    import math
    return math.ceil(x)


# Comparison
def prim_lt(a, b):
    return a < b


def prim_gt(a, b):
    return a > b


def prim_le(a, b):
    return a <= b


def prim_ge(a, b):
    return a >= b


def prim_eq(a, b):
    if isinstance(a, float) or isinstance(b, float):
        return abs(a - b) < 1e-9
    return a == b


def prim_ne(a, b):
    return not prim_eq(a, b)


# Logic
def prim_not(x):
    return not x


def prim_and(*args):
    for a in args:
        if not a:
            return False
    return True


def prim_or(*args):
    for a in args:
        if a:
            return True
    return False


# Basic data access
def prim_get(obj, key, default=None):
    """Get value from dict or list."""
    if isinstance(obj, dict):
        return obj.get(key, default)
    elif isinstance(obj, (list, tuple)):
        try:
            return obj[int(key)]
        except (IndexError, ValueError):
            return default
    return default


def prim_nth(seq, i):
    i = int(i)
    if 0 <= i < len(seq):
        return seq[i]
    return None


def prim_first(seq):
    return seq[0] if seq else None


def prim_length(seq):
    return len(seq)


def prim_list(*args):
    return list(args)


# Type checking
def prim_is_number(x):
    return isinstance(x, (int, float))


def prim_is_string(x):
    return isinstance(x, str)


def prim_is_list(x):
    return isinstance(x, (list, tuple))


def prim_is_dict(x):
    return isinstance(x, dict)


def prim_is_nil(x):
    return x is None


# Higher-order / iteration
def prim_reduce(seq, init, fn):
    """(reduce seq init fn) — fold left: fn(fn(fn(init, s0), s1), s2) ..."""
    acc = init
    for item in seq:
        acc = fn(acc, item)
    return acc


def prim_map(seq, fn):
    """(map seq fn) — apply fn to each element, return new list."""
    return [fn(item) for item in seq]


def prim_range(*args):
    """(range end), (range start end), or (range start end step) — integer range."""
    if len(args) == 1:
        return list(range(int(args[0])))
    elif len(args) == 2:
        return list(range(int(args[0]), int(args[1])))
    elif len(args) >= 3:
        return list(range(int(args[0]), int(args[1]), int(args[2])))
    return []


# Random
import random
_rng = random.Random()

def prim_rand():
    """Return random float in [0, 1)."""
    return _rng.random()

def prim_rand_int(lo, hi):
    """Return random integer in [lo, hi]."""
    return _rng.randint(int(lo), int(hi))

def prim_rand_range(lo, hi):
    """Return random float in [lo, hi)."""
    return lo + _rng.random() * (hi - lo)

def prim_map_range(val, from_lo, from_hi, to_lo, to_hi):
    """Map value from one range to another."""
    if from_hi == from_lo:
        return to_lo
    t = (val - from_lo) / (from_hi - from_lo)
    return to_lo + t * (to_hi - to_lo)


# Core primitives dict
PRIMITIVES = {
    # Arithmetic
    '+': prim_add,
    '-': prim_sub,
    '*': prim_mul,
    '/': prim_div,
    'mod': prim_mod,
    'abs': prim_abs,
    'min': prim_min,
    'max': prim_max,
    'round': prim_round,
    'floor': prim_floor,
    'ceil': prim_ceil,

    # Comparison
    '<': prim_lt,
    '>': prim_gt,
    '<=': prim_le,
    '>=': prim_ge,
    '=': prim_eq,
    '!=': prim_ne,

    # Logic
    'not': prim_not,
    'and': prim_and,
    'or': prim_or,

    # Data access
    'get': prim_get,
    'nth': prim_nth,
    'first': prim_first,
    'length': prim_length,
    'len': prim_length,
    'list': prim_list,

    # Type predicates
    'number?': prim_is_number,
    'string?': prim_is_string,
    'list?': prim_is_list,
    'dict?': prim_is_dict,
    'nil?': prim_is_nil,
    'is-nil': prim_is_nil,

    # Higher-order / iteration
    'reduce': prim_reduce,
    'fold': prim_reduce,
    'map': prim_map,
    'range': prim_range,

    # Random
    'rand': prim_rand,
    'rand-int': prim_rand_int,
    'rand-range': prim_rand_range,
    'map-range': prim_map_range,
}
