<commit_before># Copyright (c) 2014-2015, Intel Corporation All rights reserved. 
# 
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions are 
# met: 
# 
# 1. Redistributions of source code must retain the above copyright 
# notice, this list of conditions and the following disclaimer. 
#
# 2. Redistributions in binary form must reproduce the above copyright 
# notice, this list of conditions and the following disclaimer in the 
# documentation and/or other materials provided with the distribution. 
#
# 3. Neither the name of the copyright holder nor the names of its 
# contributors may be used to endorse or promote products derived from 
# this software without specific prior written permission. 
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

from __future__ import print_function

import numpy

from offload_error import OffloadError

from _pymicimpl import _pymic_impl_stream_create
from _pymicimpl import _pymic_impl_stream_destroy
from _pymicimpl import _pymic_impl_stream_allocate
from _pymicimpl import _pymic_impl_stream_deallocate
from _pymicimpl import _pymic_impl_stream_memcpy_h2d
from _pymicimpl import _pymic_impl_stream_memcpy_d2h
from _pymicimpl import _pymic_impl_stream_memcpy_d2d
from _pymicimpl import _pymic_impl_stream_ptr_translate
from _pymicimpl import _pymic_impl_invoke_kernel

from _misc import _debug as debug
from _misc import _get_order as get_order
from _tracing import _trace as trace

import pymic


class OffloadStream:
    """
    """

    _device = None
    _device_id = None
    _stream_id = None

    def __init__(self, device=None):
        # save a reference to the device
        assert device is not None
        self._device = device
        self._device_id = device.device_id

        # construct the stream
        self._stream_id = _pymic_impl_stream_create(self._device_id, 'stream')
        debug(1, 
              'created stream 0x{0:x} for device {1}'.format(self._stream_id,
                                                             self._device_id))
        
    def __del__(self):
        debug(1, 
              'destroying stream 0x{0:0x} '
              'for device {1}'.format(self._stream_id,
                                      self._device_id))
        _pymic_impl_stream_destroy(self._device_id, self._stream_id)
    
    @trace
    def sync(self):
        """Wait for all outstanding requests in this OffloadStream to complete.
           The function does not return until all in-flight requests have
           completed.

           Parameters
           ----------
           n/a

           Returns
           -------
           n/a
           
           See Also
           --------
           n/a
        """
        pass

    def __eq__(self, other):
        return (self._device == other.device and 
                self._stream_id == other.stream_id)

    def __repr__(self):
        return "OffloadStream({0}) for {1}".format(self._stream_id, 
                                                   self._device.__repr__())

    def __str__(self):
        return "stream({0}) for {1}".format(self._stream_id, str(self._device))

    def allocate_device_memory(self, nbytes, alignment=64):
        """Allocate device memory on device associated with the invoking
           stream object.  Though it is part of the stream interface,
           the operation is synchronous.  
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           nbytes : int
              Number of bytes to allocate
           alignment : int
              Alignment of the data on the target device.
           
           See Also
           --------
           deallocate_device_memory, transfer_host2device, 
           transfer_device2host, transfer_device2device

           Returns
           -------
           out : int
              Fake pointer that identifies the allocated memory

           Examples
           --------
           >>> ptr = stream.allocate_device_memory(4096)
           >>> print ptr
           140297169571840
        """
    
        device = self._device_id
    
        if nbytes <= 0:
            raise ValueError('Cannot allocate negative amount of '
                             'memory: {0}'.format(nbytes))
        
        device_ptr = _pymic_impl_stream_allocate(device, self._stream_id,
                                                 nbytes, alignment)
        debug(2, 'allocated {0} bytes on device {1} at 0x{2:x}'
                 ', alignment {3}', 
                 nbytes, device, device_ptr, alignment)
            
        return device_ptr
        
    def deallocate_device_memory(self, device_ptr):
        """Deallocate device memory previously allocated through
           allocate_device_memory.  Though it is part of the stream 
           interface, the operation is synchronous. 
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           device_ptr : int
              Fake pointer of memory do deallocate
           
           See Also
           --------
           allocate_device_memory, transfer_host2device, 
           transfer_device2host, transfer_device2device

           Returns
           -------
           None
           
           Examples
           --------
           >>> ptr = stream.allocate_device_memory(4096)
           >>> stream.deallocate_device_memory(ptr)
        """
        
        device = self._device
    
        if device_ptr is None:
            raise ValueError('Cannot deallocate None pointer')
        
        _pymic_impl_stream_deallocate(self._device_id, self._stream_id, 
                                      device_ptr)
        debug(2, 'deallocated pointer 0x{0:x} on device {1}}',
                 device, device_ptr)
        
        return None
        
    def transfer_host2device(self, host_ptr, device_ptr, 
                             nbytes, offset_host=0, offset_device=0):
        """Transfer data from a host memory location (identified by its
           raw pointer (i.e., a C pointer) to a memory region (identified 
           by its fake pointer) on the target device.  The operation is 
           executed asynchronously with stream semantics.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           host_ptr : int
              Pointer to the data on the host
           device_ptr : int 
              Fake pointer of the destination
           nbytes : int 
              Number of bytes to copy
           offset_host : int, optional, default 0
              Transfer offset (bytes) to be added to raw host pointer
           offset_device : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory.
           
           See Also
           --------
           transfer_device2host, transfer_device2device, 
           allocate_device_memory, deallocate_device_memory

           Returns
           -------
           None

           Examples
           --------
           >>> a = numpy.arange(0.0, 16.0)
           >>> nbytes = a.dtype.itemsize * a.size
           >>> ptr_a_host = a.ctypes.data
           >>> device_ptr = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_host2device(ptr_a_host, device_ptr, nbytes)
           >>> b = numpy.empty_like(a)
           >>> print b
           [  6.90762927e-310   7.73120247e-317   3.60667921e-322   
              0.00000000e+000   0.00000000e+000   0.00000000e+000   
              0.00000000e+000   0.00000000e+000   4.94065646e-324   
              9.76815212e-317   7.98912845e-317   0.00000000e+000
              5.53353523e-322   1.58101007e-322   0.00000000e+000   
              7.38839996e-317]
           # random data
           >>> ptr_b_host = b.ctypes.data
           >>> stream.transfer_device2host(device_ptr, ptr_b_host, nbytes)
           >>> stream.sync()
           >>> print b
           [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  
             12.  13.  14.  15.]
        """

        if offset_host < 0:
            raise ValueError("Negative offset passed for offset_host")
        if offset_device < 0:
            raise ValueError("Negative offset passed for offset_device")
        if device_ptr is None:
            raise ValueError('Invalid None device pointer')
        if host_ptr is None:
            raise ValueError('Invalid None host pointer')
        if nbytes <= 0:
            raise ValueError('Invalid byte count: {0}'.format(nbytes))

        debug(1, '(host -> device {0}) transferring {1} bytes '
                 '(host ptr 0x{2:x}, device ptr 0x{3:x}', 
                 self._device_id, nbytes, host_ptr, device_ptr)
        _pymic_impl_stream_memcpy_h2d(self._stream_id, host_ptr, device_ptr, 
                                      nbytes, offset_host, offset_device)
        return None

    def transfer_device2host(self, device_ptr, host_ptr, 
                             nbytes, offset_device=0, offset_host=0):
        """Transfer data from a device memory location (identified by its 
           fake pointer) to a host memory region identified by its raw pointer 
           (i.e., a C pointer)on the target device. The operation is executed 
           asynchronously with stream semantics.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           host_ptr : int
              Pointer to the data on the host
           device_ptr : int 
              Fake pointer of the destination
           nbytes : int 
              Number of bytes to copy
           offset_device : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory.
           offset_host : int, optional, default 0
              Transfer offset (bytes) to be added to raw host pointer
           
           See Also
           --------
           transfer_host2device, transfer_device2device,
           allocate_device_memory, deallocate_device_memory

           Returns
           -------
           None

           Examples
           --------
           >>> a = numpy.arange(0.0, 16.0)
           >>> nbytes = a.dtype.itemsize * a.size
           >>> ptr_a_host = a.ctypes.data
           >>> device_ptr = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_host2device(ptr_a_host, device_ptr, nbytes)
           >>> b = numpy.empty_like(a)
           >>> print b
           [  6.90762927e-310   7.73120247e-317   3.60667921e-322   
              0.00000000e+000   0.00000000e+000   0.00000000e+000   
              0.00000000e+000   0.00000000e+000   4.94065646e-324   
              9.76815212e-317   7.98912845e-317   0.00000000e+000
              5.53353523e-322   1.58101007e-322   0.00000000e+000   
              7.38839996e-317]
           # random data
           >>> ptr_b_host = b.ctypes.data
           >>> stream.transfer_device2host(device_ptr, ptr_b_host, nbytes)
           >>> stream.sync()
           >>> print b
           [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  
             12.  13.  14.  15.]
        """

        if offset_device < 0:
            raise ValueError("Negative offset passed for offset_device")
        if offset_host < 0:
            raise ValueError("Negative offset passed for offset_host")
        if device_ptr is None:
            raise ValueError('Invalid None device pointer')
        if host_ptr is None:
            raise ValueError('Invalid None host pointer')
        if nbytes <= 0:
            raise ValueError('Invalid byte count: {0}'.format(nbytes))
            
        debug(1, '(device {0} -> host) transferring {1} bytes '
                 '(device ptr 0x{2:x}, host ptr 0x{3:x}', 
                 self._device_id, nbytes, device_ptr, host_ptr)
        _pymic_impl_stream_memcpy_d2h(self._stream_id, device_ptr, host_ptr, 
                                      nbytes, offset_device, offset_host)
        return None

    def transfer_device2device(self, device_ptr_src, device_ptr_dst, 
                               nbytes, offset_device_src=0, 
                               offset_device_dst=0):
        """Transfer data from a device memory location (identified by its 
           fake pointer) to another memory region on the same device. The 
           operation is executed asynchronously with stream semantics.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           device_ptr_src : int
              Fake pointer to the source memory location
           device_ptr_dst : int 
              Fake pointer to the destination memory location
           nbytes : int 
              Number of bytes to copy
           offset_device_src : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory (source).
           offset_device_dst : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory (destination).
           
           See Also
           --------
           transfer_host2device, allocate_device_memory, 
           deallocate_device_memory

           Returns
           -------
           None

           Examples
           --------
           >>> a = numpy.arange(0.0, 16.0)
           >>> nbytes = a.dtype.itemsize * a.size
           >>> ptr_a_host = a.ctypes.data
           >>> device_ptr_1 = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_host2device(ptr_a_host, device_ptr_1, nbytes)
           >>> device_ptr_2 = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_device2device(device_ptr_1, device_ptr_2, 
                                             nbytes)
           >>> b = numpy.empty_like(a)
           [  6.95303066e-310   6.83874600e-317   3.95252517e-322   
              0.00000000e+000   9.31741387e+242   0.00000000e+000   
              0.00000000e+000   0.00000000e+000   4.94065646e-324   
              3.30519641e-317   1.72409659e+212   1.20070123e-089
              5.05907223e-085   4.87883721e+199   0.00000000e+000   
              6.78545805e-317] 
           # random data
           >>> print b
           >>> ptr_b_host = b.ctypes.data
           >>> stream.transfer_device2host(device_ptr_2, ptr_b_host, nbytes)
           >>> stream.sync()
           >>> print b
           [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  
             12.  13.  14.  15.]
        """

        if offset_device_src < 0:
            raise ValueError("Negative offset passed for offset_device_src")
        if offset_device_dst < 0:
            raise ValueError("Negative offset passed for offset_device_dst")
        if device_ptr_src is None:
            raise ValueError('Invalid None device pointer')
        if device_ptr_dst is None:
            raise ValueError('Invalid None host pointer')
        if nbytes <= 0:
            raise ValueError('Invalid byte count: {0}'.format(nbytes))
        
        debug(1, '(device {0} -> device {0}) transferring {1} bytes '
                 '(source ptr 0x{2:x}, destination ptr 0x{3:x})', 
                 self._device_id, nbytes, device_ptr_src, device_ptr_dst)
        _pymic_impl_stream_memcpy_d2d(self._stream_id, 
                                      device_ptr_src, device_ptr_dst, 
                                      nbytes, offset_device_src, 
                                      offset_device_dst)
        return None
            
    def translate_device_pointer(self, device_ptr):
        """Translate a fake pointer to a real raw pointer on the target device.
           Though it is part of the stream interface, the operation is 
           synchronous.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the <commit_msg>Bugfix: fixed conversion error for debug messages.<commit_after># Copyright (c) 2014-2015, Intel Corporation All rights reserved. 
# 
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions are 
# met: 
# 
# 1. Redistributions of source code must retain the above copyright 
# notice, this list of conditions and the following disclaimer. 
#
# 2. Redistributions in binary form must reproduce the above copyright 
# notice, this list of conditions and the following disclaimer in the 
# documentation and/or other materials provided with the distribution. 
#
# 3. Neither the name of the copyright holder nor the names of its 
# contributors may be used to endorse or promote products derived from 
# this software without specific prior written permission. 
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

from __future__ import print_function

import numpy

from offload_error import OffloadError

from _pymicimpl import _pymic_impl_stream_create
from _pymicimpl import _pymic_impl_stream_destroy
from _pymicimpl import _pymic_impl_stream_allocate
from _pymicimpl import _pymic_impl_stream_deallocate
from _pymicimpl import _pymic_impl_stream_memcpy_h2d
from _pymicimpl import _pymic_impl_stream_memcpy_d2h
from _pymicimpl import _pymic_impl_stream_memcpy_d2d
from _pymicimpl import _pymic_impl_stream_ptr_translate
from _pymicimpl import _pymic_impl_invoke_kernel

from _misc import _debug as debug
from _misc import _get_order as get_order
from _tracing import _trace as trace

import pymic


class OffloadStream:
    """
    """

    _device = None
    _device_id = None
    _stream_id = None

    def __init__(self, device=None):
        # save a reference to the device
        assert device is not None
        self._device = device
        self._device_id = device.device_id

        # construct the stream
        self._stream_id = _pymic_impl_stream_create(self._device_id, 'stream')
        debug(1, 
              'created stream 0x{0:x} for device {1}'.format(self._stream_id,
                                                             self._device_id))
        
    def __del__(self):
        debug(1, 
              'destroying stream 0x{0:0x} '
              'for device {1}'.format(self._stream_id,
                                      self._device_id))
        _pymic_impl_stream_destroy(self._device_id, self._stream_id)
    
    @trace
    def sync(self):
        """Wait for all outstanding requests in this OffloadStream to complete.
           The function does not return until all in-flight requests have
           completed.

           Parameters
           ----------
           n/a

           Returns
           -------
           n/a
           
           See Also
           --------
           n/a
        """
        pass

    def __eq__(self, other):
        return (self._device == other.device and 
                self._stream_id == other.stream_id)

    def __repr__(self):
        return "OffloadStream({0}) for {1}".format(self._stream_id, 
                                                   self._device.__repr__())

    def __str__(self):
        return "stream({0}) for {1}".format(self._stream_id, str(self._device))

    def allocate_device_memory(self, nbytes, alignment=64):
        """Allocate device memory on device associated with the invoking
           stream object.  Though it is part of the stream interface,
           the operation is synchronous.  
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           nbytes : int
              Number of bytes to allocate
           alignment : int
              Alignment of the data on the target device.
           
           See Also
           --------
           deallocate_device_memory, transfer_host2device, 
           transfer_device2host, transfer_device2device

           Returns
           -------
           out : int
              Fake pointer that identifies the allocated memory

           Examples
           --------
           >>> ptr = stream.allocate_device_memory(4096)
           >>> print ptr
           140297169571840
        """
    
        device = self._device_id
    
        if nbytes <= 0:
            raise ValueError('Cannot allocate negative amount of '
                             'memory: {0}'.format(nbytes))
        
        device_ptr = _pymic_impl_stream_allocate(device, self._stream_id,
                                                 nbytes, alignment)
        debug(2, 'allocated {0} bytes on device {1} at 0x{2:x}'
                 ', alignment {3}', 
                 nbytes, device, device_ptr, alignment)
            
        return device_ptr
        
    def deallocate_device_memory(self, device_ptr):
        """Deallocate device memory previously allocated through
           allocate_device_memory.  Though it is part of the stream 
           interface, the operation is synchronous. 
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           device_ptr : int
              Fake pointer of memory do deallocate
           
           See Also
           --------
           allocate_device_memory, transfer_host2device, 
           transfer_device2host, transfer_device2device

           Returns
           -------
           None
           
           Examples
           --------
           >>> ptr = stream.allocate_device_memory(4096)
           >>> stream.deallocate_device_memory(ptr)
        """
        
        device = self._device
    
        if device_ptr is None:
            raise ValueError('Cannot deallocate None pointer')
        
        _pymic_impl_stream_deallocate(self._device_id, self._stream_id, 
                                      device_ptr)
        debug(2, 'deallocated pointer {0} on device {1}}',
                 device, device_ptr)
        
        return None
        
    def transfer_host2device(self, host_ptr, device_ptr, 
                             nbytes, offset_host=0, offset_device=0):
        """Transfer data from a host memory location (identified by its
           raw pointer (i.e., a C pointer) to a memory region (identified 
           by its fake pointer) on the target device.  The operation is 
           executed asynchronously with stream semantics.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           host_ptr : int
              Pointer to the data on the host
           device_ptr : int 
              Fake pointer of the destination
           nbytes : int 
              Number of bytes to copy
           offset_host : int, optional, default 0
              Transfer offset (bytes) to be added to raw host pointer
           offset_device : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory.
           
           See Also
           --------
           transfer_device2host, transfer_device2device, 
           allocate_device_memory, deallocate_device_memory

           Returns
           -------
           None

           Examples
           --------
           >>> a = numpy.arange(0.0, 16.0)
           >>> nbytes = a.dtype.itemsize * a.size
           >>> ptr_a_host = a.ctypes.data
           >>> device_ptr = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_host2device(ptr_a_host, device_ptr, nbytes)
           >>> b = numpy.empty_like(a)
           >>> print b
           [  6.90762927e-310   7.73120247e-317   3.60667921e-322   
              0.00000000e+000   0.00000000e+000   0.00000000e+000   
              0.00000000e+000   0.00000000e+000   4.94065646e-324   
              9.76815212e-317   7.98912845e-317   0.00000000e+000
              5.53353523e-322   1.58101007e-322   0.00000000e+000   
              7.38839996e-317]
           # random data
           >>> ptr_b_host = b.ctypes.data
           >>> stream.transfer_device2host(device_ptr, ptr_b_host, nbytes)
           >>> stream.sync()
           >>> print b
           [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  
             12.  13.  14.  15.]
        """

        if offset_host < 0:
            raise ValueError("Negative offset passed for offset_host")
        if offset_device < 0:
            raise ValueError("Negative offset passed for offset_device")
        if device_ptr is None:
            raise ValueError('Invalid None device pointer')
        if host_ptr is None:
            raise ValueError('Invalid None host pointer')
        if nbytes <= 0:
            raise ValueError('Invalid byte count: {0}'.format(nbytes))

        debug(1, '(host -> device {0}) transferring {1} bytes '
                 '(host ptr 0x{2:x}, device ptr {3}', 
                 self._device_id, nbytes, host_ptr, device_ptr)
        _pymic_impl_stream_memcpy_h2d(self._stream_id, host_ptr, device_ptr, 
                                      nbytes, offset_host, offset_device)
        return None

    def transfer_device2host(self, device_ptr, host_ptr, 
                             nbytes, offset_device=0, offset_host=0):
        """Transfer data from a device memory location (identified by its 
           fake pointer) to a host memory region identified by its raw pointer 
           (i.e., a C pointer)on the target device. The operation is executed 
           asynchronously with stream semantics.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           host_ptr : int
              Pointer to the data on the host
           device_ptr : int 
              Fake pointer of the destination
           nbytes : int 
              Number of bytes to copy
           offset_device : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory.
           offset_host : int, optional, default 0
              Transfer offset (bytes) to be added to raw host pointer
           
           See Also
           --------
           transfer_host2device, transfer_device2device,
           allocate_device_memory, deallocate_device_memory

           Returns
           -------
           None

           Examples
           --------
           >>> a = numpy.arange(0.0, 16.0)
           >>> nbytes = a.dtype.itemsize * a.size
           >>> ptr_a_host = a.ctypes.data
           >>> device_ptr = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_host2device(ptr_a_host, device_ptr, nbytes)
           >>> b = numpy.empty_like(a)
           >>> print b
           [  6.90762927e-310   7.73120247e-317   3.60667921e-322   
              0.00000000e+000   0.00000000e+000   0.00000000e+000   
              0.00000000e+000   0.00000000e+000   4.94065646e-324   
              9.76815212e-317   7.98912845e-317   0.00000000e+000
              5.53353523e-322   1.58101007e-322   0.00000000e+000   
              7.38839996e-317]
           # random data
           >>> ptr_b_host = b.ctypes.data
           >>> stream.transfer_device2host(device_ptr, ptr_b_host, nbytes)
           >>> stream.sync()
           >>> print b
           [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  
             12.  13.  14.  15.]
        """

        if offset_device < 0:
            raise ValueError("Negative offset passed for offset_device")
        if offset_host < 0:
            raise ValueError("Negative offset passed for offset_host")
        if device_ptr is None:
            raise ValueError('Invalid None device pointer')
        if host_ptr is None:
            raise ValueError('Invalid None host pointer')
        if nbytes <= 0:
            raise ValueError('Invalid byte count: {0}'.format(nbytes))
            
        debug(1, '(device {0} -> host) transferring {1} bytes '
                 '(device ptr {2}, host ptr 0x{3:x}', 
                 self._device_id, nbytes, device_ptr, host_ptr)
        _pymic_impl_stream_memcpy_d2h(self._stream_id, device_ptr, host_ptr, 
                                      nbytes, offset_device, offset_host)
        return None

    def transfer_device2device(self, device_ptr_src, device_ptr_dst, 
                               nbytes, offset_device_src=0, 
                               offset_device_dst=0):
        """Transfer data from a device memory location (identified by its 
           fake pointer) to another memory region on the same device. The 
           operation is executed asynchronously with stream semantics.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
                    high-level interfaces of OffloadArray instead.
           
           Parameters
           ----------
           device_ptr_src : int
              Fake pointer to the source memory location
           device_ptr_dst : int 
              Fake pointer to the destination memory location
           nbytes : int 
              Number of bytes to copy
           offset_device_src : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory (source).
           offset_device_dst : int, optional, default 0
              Transfer offset (bytes) to be added to the address of the device
              memory (destination).
           
           See Also
           --------
           transfer_host2device, allocate_device_memory, 
           deallocate_device_memory

           Returns
           -------
           None

           Examples
           --------
           >>> a = numpy.arange(0.0, 16.0)
           >>> nbytes = a.dtype.itemsize * a.size
           >>> ptr_a_host = a.ctypes.data
           >>> device_ptr_1 = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_host2device(ptr_a_host, device_ptr_1, nbytes)
           >>> device_ptr_2 = stream.allocate_device_memory(nbytes)
           >>> stream.transfer_device2device(device_ptr_1, device_ptr_2, 
                                             nbytes)
           >>> b = numpy.empty_like(a)
           [  6.95303066e-310   6.83874600e-317   3.95252517e-322   
              0.00000000e+000   9.31741387e+242   0.00000000e+000   
              0.00000000e+000   0.00000000e+000   4.94065646e-324   
              3.30519641e-317   1.72409659e+212   1.20070123e-089
              5.05907223e-085   4.87883721e+199   0.00000000e+000   
              6.78545805e-317] 
           # random data
           >>> print b
           >>> ptr_b_host = b.ctypes.data
           >>> stream.transfer_device2host(device_ptr_2, ptr_b_host, nbytes)
           >>> stream.sync()
           >>> print b
           [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  
             12.  13.  14.  15.]
        """

        if offset_device_src < 0:
            raise ValueError("Negative offset passed for offset_device_src")
        if offset_device_dst < 0:
            raise ValueError("Negative offset passed for offset_device_dst")
        if device_ptr_src is None:
            raise ValueError('Invalid None device pointer')
        if device_ptr_dst is None:
            raise ValueError('Invalid None host pointer')
        if nbytes <= 0:
            raise ValueError('Invalid byte count: {0}'.format(nbytes))
        
        debug(1, '(device {0} -> device {0}) transferring {1} bytes '
                 '(source ptr {2}, destination ptr {3})', 
                 self._device_id, nbytes, device_ptr_src, device_ptr_dst)
        _pymic_impl_stream_memcpy_d2d(self._stream_id, 
                                      device_ptr_src, device_ptr_dst, 
                                      nbytes, offset_device_src, 
                                      offset_device_dst)
        return None
            
    def translate_device_pointer(self, device_ptr):
        """Translate a fake pointer to a real raw pointer on the target device.
           Though it is part of the stream interface, the operation is 
           synchronous.
           
           Caution: this is a low-level function, do not use it unless you
                    have a very specific reason to do so.  Better use the 
{'fraction_non_alphanumeric': 0.07367180264376526, 'fraction_numerical': 0.04459599786702591, 'mean_word_length': 1.6107854630715124, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 243, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 38, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1323623', 'n_tokens_mistral': 10466, 'n_tokens_neox': 9251, 'n_words': 2958}