# Path: gym3/types.py
# INTEGER_DTYPE_NAMES = set(
#     ["int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"]
# )
# FLOAT_DTYPE_NAMES = set(["float32", "float64"])
# DTYPE_NAME_TO_MAX_VALUE = {}
# DTYPE_NAME_TO_BIT_WIDTH = {}
# def pod_equals(x, y):
#     def __init__(self):
#     def __init__(self, dtype_name: str = "float32"):
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, n: int, dtype_name: str = "int64") -> None:
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, eltype: ScalarType, shape: Tuple) -> None:
#     def ndim(self):
#     def size(self):
#     def __repr__(self):
#     def __str__(self):
# def discrete_scalar(n: int) -> TensorType:
#     def __init__(self, **name2type: ValType) -> None:
#     def __repr__(self):
#     def __str__(self):
#     def __len__(self):
#     def keys(self):
#     def values(self):
#     def items(self):
#     def __getitem__(self, key):
#     def __contains__(self, key):
# def multimap(f: Callable, *xs: Any) -> Any:
# class ScalarType:
# class Real(ScalarType):
# class Discrete(ScalarType):
# class ValType:
# class TensorType(ValType):
# class DictType(ValType):
# 
# Path: gym3/env.py
# class Env:
#     """
#     An interface for reinforcement learning environments.
# 
#     :param ob_space: ValType representing the valid observations generated by the environment
#     :param ac_space: ValType representing the valid actions that an agent can take in the environment
#     :param num: number of simultaneous episodes
#     """
# 
#     def __init__(self, ob_space: ValType, ac_space: ValType, num: int):
#         self.ob_space = ob_space
#         self.ac_space = ac_space
#         self.num = num
# 
#     def observe(self) -> Tuple[Any, Any, Any]:
#         """
#         Structured data that's accessible to the agent
# 
#         Can be called zero or more times per act() call; it is idempotent and does
#         not affect the environment.  Returns the observation after the last act()
#         call has affected the environment or the initial state if no act()
#         call has been made.
# 
#         The following snippet shows what the start of a rollout looks like, and
#         the usual convention for indexing the values:
# 
#         env = Env()
#         # initial reward is ignored; didn't depend on actions
#         _reward, ob_0, first_0 = env.observe()
#         env.act(ac_0)
#         reward_0, ob_1, first_1 = env.observe()
#         env.act(ac_1)
# 
#         Note that the initial reward `_reward` is ignored by algorithms, since it
#         didn't depend on the actions. And that first_0 is always True since the
#         environment was just created.
# 
#         :returns:
#             reward: (float array) last reward, shaped (num,)
#             ob: observation matching ob_space, with (num,) prepended to the shape of each leaf
#             first: (bool array) whether new episode just started, shaped (num,)
#         """
#         raise NotImplementedError
# 
#     def get_info(self) -> List[Dict]:
#         """
#         Return unstructured diagnostics that aren't accessible to the agent
#         Per-episode stats, rendered images, etc.
#         Corresponds to same timestep as the observation from observe().
# 
#         :returns: a list of dictionaries with length `self.num`
#         """
#         return [{} for _ in range(self.num)]
# 
#     def act(self, ac: Any) -> None:
#         """
#         Apply an action
# 
#         :param ac: action matching ac_space, with (num,) prepended to the shape of each leaf
#         """
#         raise NotImplementedError
# 
#     def callmethod(
#         self, method: str, *args: Sequence[Any], **kwargs: Sequence[Any]
#     ) -> List[Any]:
#         """
#         Call a method on the underlying python object that offers the Gym3 interface.
#         By default, this is the Env instance (self), but this can be overridden if you are
#         encapsulating another object or if your wrapper wishes to handle the method call.
# 
#         :param method: name of method to call on the base object
#         :param *args, **kwargs: the value of each argument must be a list with length equal to `self.num`
# 
#         :returns: A list of objects, with length equal to `self.num`
#         """
#         return getattr(self, method)(*args, **kwargs)


import collections
import copy
import os
import platform
import threading
import numpy as np
from typing import Any, Dict, List, Optional, Sequence, Tuple
from cffi import FFI
from gym3 import types
from gym3.env import Env
        # convert to gym3 types
        name_to_tensortype = {}
        specs = []
        for i in range(count):
            c_tt = c_tensortypes[i]

            name = self._ffi.string(c_tt.name).decode("utf8")
            shape = tuple(c_tt.shape[j] for j in range(c_tt.ndim))

            if c_tt.scalar_type == self._c_lib.LIBENV_SCALAR_TYPE_REAL:
                if c_tt.dtype == self._c_lib.LIBENV_DTYPE_FLOAT32:
                    dtype = np.dtype("float32")
                    low = c_tt.low.float32
                    high = c_tt.high.float32
                else:
                    assert False, "unrecognized dtype for real"
                tensortype = types.TensorType(eltype=types.Real(), shape=shape)
            elif c_tt.scalar_type == self._c_lib.LIBENV_SCALAR_TYPE_DISCRETE:
                if c_tt.dtype == self._c_lib.LIBENV_DTYPE_UINT8:
                    dtype = np.dtype("uint8")
                    low = c_tt.low.uint8
                    high = c_tt.high.uint8
                elif c_tt.dtype == self._c_lib.LIBENV_DTYPE_INT32:
                    dtype = np.dtype("int32")
                    low = c_tt.low.int32
                    high = c_tt.high.int32
                else:
                    assert False, "unrecognized dtype for discrete"
                assert low == 0 and high >= 0, "discrete low/high bounds are incorrect"
                tensortype = types.TensorType(

# Path: gym3/types.py
# INTEGER_DTYPE_NAMES = set(
#     ["int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"]
# )
# FLOAT_DTYPE_NAMES = set(["float32", "float64"])
# DTYPE_NAME_TO_MAX_VALUE = {}
# DTYPE_NAME_TO_BIT_WIDTH = {}
# def pod_equals(x, y):
#     def __init__(self):
#     def __init__(self, dtype_name: str = "float32"):
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, n: int, dtype_name: str = "int64") -> None:
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, eltype: ScalarType, shape: Tuple) -> None:
#     def ndim(self):
#     def size(self):
#     def __repr__(self):
#     def __str__(self):
# def discrete_scalar(n: int) -> TensorType:
#     def __init__(self, **name2type: ValType) -> None:
#     def __repr__(self):
#     def __str__(self):
#     def __len__(self):
#     def keys(self):
#     def values(self):
#     def items(self):
#     def __getitem__(self, key):
#     def __contains__(self, key):
# def multimap(f: Callable, *xs: Any) -> Any:
# class ScalarType:
# class Real(ScalarType):
# class Discrete(ScalarType):
# class ValType:
# class TensorType(ValType):
# class DictType(ValType):
# 
# Path: gym3/env.py
# class Env:
#     """
#     An interface for reinforcement learning environments.
# 
#     :param ob_space: ValType representing the valid observations generated by the environment
#     :param ac_space: ValType representing the valid actions that an agent can take in the environment
#     :param num: number of simultaneous episodes
#     """
# 
#     def __init__(self, ob_space: ValType, ac_space: ValType, num: int):
#         self.ob_space = ob_space
#         self.ac_space = ac_space
#         self.num = num
# 
#     def observe(self) -> Tuple[Any, Any, Any]:
#         """
#         Structured data that's accessible to the agent
# 
#         Can be called zero or more times per act() call; it is idempotent and does
#         not affect the environment.  Returns the observation after the last act()
#         call has affected the environment or the initial state if no act()
#         call has been made.
# 
#         The following snippet shows what the start of a rollout looks like, and
#         the usual convention for indexing the values:
# 
#         env = Env()
#         # initial reward is ignored; didn't depend on actions
#         _reward, ob_0, first_0 = env.observe()
#         env.act(ac_0)
#         reward_0, ob_1, first_1 = env.observe()
#         env.act(ac_1)
# 
#         Note that the initial reward `_reward` is ignored by algorithms, since it
#         didn't depend on the actions. And that first_0 is always True since the
#         environment was just created.
# 
#         :returns:
#             reward: (float array) last reward, shaped (num,)
#             ob: observation matching ob_space, with (num,) prepended to the shape of each leaf
#             first: (bool array) whether new episode just started, shaped (num,)
#         """
#         raise NotImplementedError
# 
#     def get_info(self) -> List[Dict]:
#         """
#         Return unstructured diagnostics that aren't accessible to the agent
#         Per-episode stats, rendered images, etc.
#         Corresponds to same timestep as the observation from observe().
# 
#         :returns: a list of dictionaries with length `self.num`
#         """
#         return [{} for _ in range(self.num)]
# 
#     def act(self, ac: Any) -> None:
#         """
#         Apply an action
# 
#         :param ac: action matching ac_space, with (num,) prepended to the shape of each leaf
#         """
#         raise NotImplementedError
# 
#     def callmethod(
#         self, method: str, *args: Sequence[Any], **kwargs: Sequence[Any]
#     ) -> List[Any]:
#         """
#         Call a method on the underlying python object that offers the Gym3 interface.
#         By default, this is the Env instance (self), but this can be overridden if you are
#         encapsulating another object or if your wrapper wishes to handle the method call.
# 
#         :param method: name of method to call on the base object
#         :param *args, **kwargs: the value of each argument must be a list with length equal to `self.num`
# 
#         :returns: A list of objects, with length equal to `self.num`
#         """
#         return getattr(self, method)(*args, **kwargs)


# Path: gym3/libenv.py
import collections
import copy
import os
import platform
import threading
import numpy as np
from typing import Any, Dict, List, Optional, Sequence, Tuple
from cffi import FFI
from gym3 import types
from gym3.env import Env

        # convert to gym3 types
        name_to_tensortype = {}
        specs = []
        for i in range(count):
            c_tt = c_tensortypes[i]

            name = self._ffi.string(c_tt.name).decode("utf8")
            shape = tuple(c_tt.shape[j] for j in range(c_tt.ndim))

            if c_tt.scalar_type == self._c_lib.LIBENV_SCALAR_TYPE_REAL:
                if c_tt.dtype == self._c_lib.LIBENV_DTYPE_FLOAT32:
                    dtype = np.dtype("float32")
                    low = c_tt.low.float32
                    high = c_tt.high.float32
                else:
                    assert False, "unrecognized dtype for real"
                tensortype = types.TensorType(eltype=types.Real(), shape=shape)
            elif c_tt.scalar_type == self._c_lib.LIBENV_SCALAR_TYPE_DISCRETE:
                if c_tt.dtype == self._c_lib.LIBENV_DTYPE_UINT8:
                    dtype = np.dtype("uint8")
                    low = c_tt.low.uint8
                    high = c_tt.high.uint8
                elif c_tt.dtype == self._c_lib.LIBENV_DTYPE_INT32:
                    dtype = np.dtype("int32")
                    low = c_tt.low.int32
                    high = c_tt.high.int32
                else:
                    assert False, "unrecognized dtype for discrete"
                assert low == 0 and high >= 0, "discrete low/high bounds are incorrect"
                tensortype = types.TensorType(

                    eltype=types.Discrete(n=high + 1, dtype_name=dtype.name),