code: def eval_upto(self, e, n, cast_to=None, **kwargs):
          
          concrete_val = _concrete_value(e)
          if concrete_val is not None:
              return [self._cast_to(e, concrete_val, cast_to)]
  
          cast_vals = [self._cast_to(e, v, cast_to) for v in self._eval(e, n, **kwargs)]
          if len(cast_vals) == 0:
              raise SimUnsatError('Not satisfiable: %s, expected up to %d solutions' % (e.shallow_repr(), n))
          return cast_vals
docs: Evaluate an expression, using the solver if necessary. Returns primitives as specified by the `cast_to`
          parameter. Only certain primitives are supported, check the implementation of `_cast_to` to see which ones.
  
          :param e: the expression
          :param n: the number of desired solutions
          :param extra_constraints: extra constraints to apply to the solver
          :param exact: if False, returns approximate solutions
          :param cast_to: A type to cast the resulting values to
          :return: a tuple of the solutions, in the form of Python primitives
          :rtype: tuple