
class Parameter:
    """A single named parameter. Example: theta = Parameter("theta")"""
    def __init__(self, name):
        self.name = name
        self._value = None

    def is_bound(self):
        return self._value is not None

    def value(self):
        if self._value is None:
            raise ValueError(f"Parameter '{self.name}' has not been bound yet.")
        return self._value

    def __repr__(self):
        if self._value is not None:
            return f"Parameter({self.name}={self._value})"
        return f"Parameter({self.name})"

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return isinstance(other, Parameter) and self.name == other.name


class ParameterVector:
    """A list of parameters. Example: theta = ParameterVector("theta", 3)"""
    def __init__(self, name, length):
        self.name = name
        self.length = length
        self.params = [Parameter(f"{name}[{i}]") for i in range(length)]

    def __getitem__(self, index):
        return self.params[index]

    def __len__(self):
        return self.length

    def __iter__(self):
        return iter(self.params)

    def __repr__(self):
        return f"ParameterVector({self.name}, length={self.length})"
