# Path: numcodecs/abc.py
# class Codec:
#     """Codec abstract base class."""
# 
#     # override in sub-class
#     codec_id = None
#     """Codec identifier."""
# 
#     @abstractmethod
#     def encode(self, buf):  # pragma: no cover
#         """Encode data in `buf`.
# 
#         Parameters
#         ----------
#         buf : buffer-like
#             Data to be encoded. May be any object supporting the new-style
#             buffer protocol.
# 
#         Returns
#         -------
#         enc : buffer-like
#             Encoded data. May be any object supporting the new-style buffer
#             protocol.
#         """
# 
#     @abstractmethod
#     def decode(self, buf, out=None):  # pragma: no cover
#         """Decode data in `buf`.
# 
#         Parameters
#         ----------
#         buf : buffer-like
#             Encoded data. May be any object supporting the new-style buffer
#             protocol.
#         out : buffer-like, optional
#             Writeable buffer to store decoded data. N.B. if provided, this buffer must
#             be exactly the right size to store the decoded data.
# 
#         Returns
#         -------
#         dec : buffer-like
#             Decoded data. May be any object supporting the new-style
#             buffer protocol.
#         """
# 
#     def get_config(self):
#         """Return a dictionary holding configuration parameters for this
#         codec. Must include an 'id' field with the codec identifier. All
#         values must be compatible with JSON encoding."""
# 
#         # override in sub-class if need special encoding of config values
# 
#         # setup config object
#         config = dict(id=self.codec_id)
# 
#         # by default, assume all non-private members are configuration
#         # parameters - override this in sub-class if not the case
#         for k in self.__dict__:
#             if not k.startswith('_'):
#                 config[k] = getattr(self, k)
# 
#         return config
# 
#     @classmethod
#     def from_config(cls, config):
#         """Instantiate codec from a configuration object."""
#         # N.B., assume at this point the 'id' field has been removed from
#         # the config object
# 
#         # override in sub-class if need special decoding of config values
# 
#         # by default, assume constructor accepts configuration parameters as
#         # keyword arguments without any special decoding
#         return cls(**config)
# 
#     def __eq__(self, other):
#         # override in sub-class if need special equality comparison
#         try:
#             return self.get_config() == other.get_config()
#         except AttributeError:
#             return False
# 
#     def __repr__(self):
# 
#         # override in sub-class if need special representation
# 
#         # by default, assume all non-private members are configuration
#         # parameters and valid keyword arguments to constructor function
# 
#         r = '%s(' % type(self).__name__
#         params = ['{}={!r}'.format(k, getattr(self, k))
#                   for k in sorted(self.__dict__)
#                   if not k.startswith('_')]
#         r += ', '.join(params) + ')'
#         return r
# 
# Path: numcodecs/compat.py
# def ensure_ndarray(buf):
#     """Convenience function to coerce `buf` to a numpy array, if it is not already a
#     numpy array.
# 
#     Parameters
#     ----------
#     buf : array-like or bytes-like
#         A numpy array or any object exporting a buffer interface.
# 
#     Returns
#     -------
#     arr : ndarray
#         A numpy array, sharing memory with `buf`.
# 
#     Notes
#     -----
#     This function will not create a copy under any circumstances, it is guaranteed to
#     return a view on memory exported by `buf`.
# 
#     """
# 
#     if isinstance(buf, np.ndarray):
#         # already a numpy array
#         arr = buf
# 
#     elif isinstance(buf, array.array) and buf.typecode in 'cu':
#         # Guard condition, do not support array.array with unicode type, this is
#         # problematic because numpy does not support it on all platforms. Also do not
#         # support char as it was removed in Python 3.
#         raise TypeError('array.array with char or unicode type is not supported')
# 
#     else:
# 
#         # N.B., first take a memoryview to make sure that we subsequently create a
#         # numpy array from a memory buffer with no copy
#         mem = memoryview(buf)
# 
#         # instantiate array from memoryview, ensures no copy
#         arr = np.array(mem, copy=False)
# 
#     return arr
# 
# def ndarray_copy(src, dst):
#     """Copy the contents of the array from `src` to `dst`."""
# 
#     if dst is None:
#         # no-op
#         return src
# 
#     # ensure ndarrays
#     src = ensure_ndarray(src)
#     dst = ensure_ndarray(dst)
# 
#     # flatten source array
#     src = src.reshape(-1, order='A')
# 
#     # ensure same data type
#     if dst.dtype != object:
#         src = src.view(dst.dtype)
# 
#     # reshape source to match destination
#     if src.shape != dst.shape:
#         if dst.flags.f_contiguous:
#             order = 'F'
#         else:
#             order = 'C'
#         src = src.reshape(dst.shape, order=order)
# 
#     # copy via numpy
#     np.copyto(dst, src)
# 
#     return dst


import math
import numpy as np
from .abc import Codec
from .compat import ensure_ndarray, ndarray_copy
        if astype is None:
            self.astype = self.dtype
        else:
            self.astype = np.dtype(astype)
        if self.dtype.kind != 'f' or self.astype.kind != 'f':
            raise ValueError('only floating point data types are supported')

    def encode(self, buf):

        # normalise input
        arr = ensure_ndarray(buf).view(self.dtype)

        # apply scaling
        precision = 10. ** -self.digits
        exp = math.log(precision, 10)
        if exp < 0:
            exp = int(math.floor(exp))
        else:
            exp = int(math.ceil(exp))
        bits = math.ceil(math.log(10. ** -exp, 2))
        scale = 2. ** bits
        enc = np.around(scale * arr) / scale

        # cast dtype
        enc = enc.astype(self.astype, copy=False)

        return enc

    def decode(self, buf, out=None):
        # filter is lossy, decoding is no-op

# Path: numcodecs/abc.py
# class Codec:
#     """Codec abstract base class."""
# 
#     # override in sub-class
#     codec_id = None
#     """Codec identifier."""
# 
#     @abstractmethod
#     def encode(self, buf):  # pragma: no cover
#         """Encode data in `buf`.
# 
#         Parameters
#         ----------
#         buf : buffer-like
#             Data to be encoded. May be any object supporting the new-style
#             buffer protocol.
# 
#         Returns
#         -------
#         enc : buffer-like
#             Encoded data. May be any object supporting the new-style buffer
#             protocol.
#         """
# 
#     @abstractmethod
#     def decode(self, buf, out=None):  # pragma: no cover
#         """Decode data in `buf`.
# 
#         Parameters
#         ----------
#         buf : buffer-like
#             Encoded data. May be any object supporting the new-style buffer
#             protocol.
#         out : buffer-like, optional
#             Writeable buffer to store decoded data. N.B. if provided, this buffer must
#             be exactly the right size to store the decoded data.
# 
#         Returns
#         -------
#         dec : buffer-like
#             Decoded data. May be any object supporting the new-style
#             buffer protocol.
#         """
# 
#     def get_config(self):
#         """Return a dictionary holding configuration parameters for this
#         codec. Must include an 'id' field with the codec identifier. All
#         values must be compatible with JSON encoding."""
# 
#         # override in sub-class if need special encoding of config values
# 
#         # setup config object
#         config = dict(id=self.codec_id)
# 
#         # by default, assume all non-private members are configuration
#         # parameters - override this in sub-class if not the case
#         for k in self.__dict__:
#             if not k.startswith('_'):
#                 config[k] = getattr(self, k)
# 
#         return config
# 
#     @classmethod
#     def from_config(cls, config):
#         """Instantiate codec from a configuration object."""
#         # N.B., assume at this point the 'id' field has been removed from
#         # the config object
# 
#         # override in sub-class if need special decoding of config values
# 
#         # by default, assume constructor accepts configuration parameters as
#         # keyword arguments without any special decoding
#         return cls(**config)
# 
#     def __eq__(self, other):
#         # override in sub-class if need special equality comparison
#         try:
#             return self.get_config() == other.get_config()
#         except AttributeError:
#             return False
# 
#     def __repr__(self):
# 
#         # override in sub-class if need special representation
# 
#         # by default, assume all non-private members are configuration
#         # parameters and valid keyword arguments to constructor function
# 
#         r = '%s(' % type(self).__name__
#         params = ['{}={!r}'.format(k, getattr(self, k))
#                   for k in sorted(self.__dict__)
#                   if not k.startswith('_')]
#         r += ', '.join(params) + ')'
#         return r
# 
# Path: numcodecs/compat.py
# def ensure_ndarray(buf):
#     """Convenience function to coerce `buf` to a numpy array, if it is not already a
#     numpy array.
# 
#     Parameters
#     ----------
#     buf : array-like or bytes-like
#         A numpy array or any object exporting a buffer interface.
# 
#     Returns
#     -------
#     arr : ndarray
#         A numpy array, sharing memory with `buf`.
# 
#     Notes
#     -----
#     This function will not create a copy under any circumstances, it is guaranteed to
#     return a view on memory exported by `buf`.
# 
#     """
# 
#     if isinstance(buf, np.ndarray):
#         # already a numpy array
#         arr = buf
# 
#     elif isinstance(buf, array.array) and buf.typecode in 'cu':
#         # Guard condition, do not support array.array with unicode type, this is
#         # problematic because numpy does not support it on all platforms. Also do not
#         # support char as it was removed in Python 3.
#         raise TypeError('array.array with char or unicode type is not supported')
# 
#     else:
# 
#         # N.B., first take a memoryview to make sure that we subsequently create a
#         # numpy array from a memory buffer with no copy
#         mem = memoryview(buf)
# 
#         # instantiate array from memoryview, ensures no copy
#         arr = np.array(mem, copy=False)
# 
#     return arr
# 
# def ndarray_copy(src, dst):
#     """Copy the contents of the array from `src` to `dst`."""
# 
#     if dst is None:
#         # no-op
#         return src
# 
#     # ensure ndarrays
#     src = ensure_ndarray(src)
#     dst = ensure_ndarray(dst)
# 
#     # flatten source array
#     src = src.reshape(-1, order='A')
# 
#     # ensure same data type
#     if dst.dtype != object:
#         src = src.view(dst.dtype)
# 
#     # reshape source to match destination
#     if src.shape != dst.shape:
#         if dst.flags.f_contiguous:
#             order = 'F'
#         else:
#             order = 'C'
#         src = src.reshape(dst.shape, order=order)
# 
#     # copy via numpy
#     np.copyto(dst, src)
# 
#     return dst


# Path: numcodecs/quantize.py
import math
import numpy as np
from .abc import Codec
from .compat import ensure_ndarray, ndarray_copy

        if astype is None:
            self.astype = self.dtype
        else:
            self.astype = np.dtype(astype)
        if self.dtype.kind != 'f' or self.astype.kind != 'f':
            raise ValueError('only floating point data types are supported')

    def encode(self, buf):

        # normalise input
        arr = ensure_ndarray(buf).view(self.dtype)

        # apply scaling
        precision = 10. ** -self.digits
        exp = math.log(precision, 10)
        if exp < 0:
            exp = int(math.floor(exp))
        else:
            exp = int(math.ceil(exp))
        bits = math.ceil(math.log(10. ** -exp, 2))
        scale = 2. ** bits
        enc = np.around(scale * arr) / scale

        # cast dtype
        enc = enc.astype(self.astype, copy=False)

        return enc

    def decode(self, buf, out=None):
        # filter is lossy, decoding is no-op

        dec = ensure_ndarray(buf).view(self.astype)