# Path: blimpy/utils.py
# def unpack_1to8(data):
#     """ Promote 1-bit unisgned data into 8-bit unsigned data.
# 
#     Args:
#         data: Numpy array with dtype == uint8
#     """
#     return np.unpackbits(data)
# 
# def unpack_2to8(data):
#     """ Promote 2-bit unisgned data into 8-bit unsigned data.
# 
#     Args:
#         data: Numpy array with dtype == uint8
# 
#     Notes:
#         DATA MUST BE LOADED as np.array() with dtype='uint8'.
# 
#         This works with some clever shifting and AND / OR operations.
#         Data is LOADED as 8-bit, then promoted to 32-bits:
#         /ABCD EFGH/ (8 bits of data)
#         /0000 0000/0000 0000/0000 0000/ABCD EFGH/ (8 bits of data as a 32-bit word)
# 
#         Once promoted, we can do some shifting, AND and OR operations:
#         /0000 0000/0000 ABCD/EFGH 0000/0000 0000/ (shifted << 12)
#         /0000 0000/0000 ABCD/EFGH 0000/ABCD EFGH/ (bitwise OR of previous two lines)
#         /0000 0000/0000 ABCD/0000 0000/0000 EFGH/ (bitwise AND with mask 0xF000F)
#         /0000 00AB/CD00 0000/0000 00EF/GH00 0000/ (prev. line shifted << 6)
#         /0000 00AB/CD00 ABCD/0000 00EF/GH00 EFGH/ (bitwise OR of previous two lines)
#         /0000 00AB/0000 00CD/0000 00EF/0000 00GH/ (bitwise AND with 0x3030303)
# 
#         Then we change the view of the data to interpret it as 4x8 bit:
#         [000000AB, 000000CD, 000000EF, 000000GH]  (change view from 32-bit to 4x8-bit)
# 
#         The converted bits are then mapped to values in the range [-40, 40] according to a lookup chart.
#         The mapping is based on specifications in the breakthough docs:
#         https://github.com/UCBerkeleySETI/breakthrough/blob/master/doc/RAW-File-Format.md
# 
#     """
#     two_eight_lookup = {0: 40,
#                         1: 12,
#                         2: -12,
#                         3: -40}
# 
#     tmp = data.astype(np.uint32)
#     tmp = (tmp | (tmp << 12)) & 0xF000F
#     tmp = (tmp | (tmp << 6)) & 0x3030303
#     tmp = tmp.byteswap()
#     tmp = tmp.view('uint8')
#     mapped = np.array(tmp, dtype=np.int8)
#     for k, v in two_eight_lookup.items():
#         mapped[tmp == k] = v
#     return mapped
# 
# def unpack_4to8(data):
#     """ Promote 2-bit unisgned data into 8-bit unsigned data.
# 
#     Args:
#         data: Numpy array with dtype == uint8
# 
#     Notes:
#         # The process is this:
#         # ABCDEFGH [Bits of one 4+4-bit value]
#         # 00000000ABCDEFGH [astype(uint16)]
#         # 0000ABCDEFGH0000 [<< 4]
#         # 0000ABCDXXXXEFGH [bitwise 'or' of previous two lines]
#         # 0000111100001111 [0x0F0F]
#         # 0000ABCD0000EFGH [bitwise 'and' of previous two lines]
#         # ABCD0000EFGH0000 [<< 4]
#         # which effectively pads the two 4-bit values with zeros on the right
#         # Note: This technique assumes LSB-first ordering
#     """
# 
#     tmpdata = data.astype(np.int16)  # np.empty(upshape, dtype=np.int16)
#     tmpdata = (tmpdata | (tmpdata << 4)) & 0x0F0F
#     # tmpdata = tmpdata << 4 # Shift into high bits to avoid needing to sign extend
#     updata = tmpdata.byteswap()
#     return updata.view(data.dtype)
# 
# def unpack(data, nbit):
#     """upgrade data from nbits to 8bits
# 
#     Notes: Pretty sure this function is a little broken!
#     """
#     if nbit > 8:
#         raise ValueError("unpack: nbit must be <= 8")
#     if 8 % nbit != 0:
#         raise ValueError("unpack: nbit must divide into 8")
#     if data.dtype not in (np.uint8, np.int8):
#         raise TypeError("unpack: dtype must be 8-bit")
#     if nbit == 8:
#         return data
#     if nbit == 4:
#         data = unpack_4to8(data)
#         return data
#     if nbit == 2:
#         data = unpack_2to8(data)
#         return data
#     if nbit == 1:
#         data = unpack_1to8(data)
#         return data
# 
#     return None ## SHOULD NEVER HAPPEN


from blimpy.utils import unpack_1to8, unpack_2to8, unpack_4to8, unpack
import numpy as np
import pytest
    a = np.array([0b00011011, 0b11100100], dtype=np.uint8)
    b = np.array([40, 12, -12, -40, -40, -12, 12, 40], dtype=np.int8)

    c = unpack_2to8(a)

    assert np.allclose(b, c)

def test_4to8():
    # Create an array that should come out as [0, 1, 2, 3, 3, 2, 1, 0]
    # In binary, this is [00, 01, 10, 11, 11, 10, 01, 00]
    # Convert to 8-bit, this is [0b00011011, 0b11100100]

    # Test 4-bit unpack
    a = np.array([0b00000001, 0b00100011], dtype=np.uint8)
    b = np.array([0, 1, 2, 3], dtype=np.uint8)
    c = unpack_4to8(a)
    assert np.allclose(b, c)

def test_unpack():

    # Test 2-bit unpack
    a = np.array([0b00011011, 0b11100100], dtype=np.uint8)
    b = np.array([40, 12, -12, -40, -40, -12, 12, 40], dtype=np.int8)
    c = unpack(a, 2)
    assert np.allclose(b, c)

    # Catch exceptions
    with pytest.raises(ValueError):
        unpack(a, 16)  # nbit <= 8 is reqd
    with pytest.raises(ValueError):

# Path: blimpy/utils.py
# def unpack_1to8(data):
#     """ Promote 1-bit unisgned data into 8-bit unsigned data.
# 
#     Args:
#         data: Numpy array with dtype == uint8
#     """
#     return np.unpackbits(data)
# 
# def unpack_2to8(data):
#     """ Promote 2-bit unisgned data into 8-bit unsigned data.
# 
#     Args:
#         data: Numpy array with dtype == uint8
# 
#     Notes:
#         DATA MUST BE LOADED as np.array() with dtype='uint8'.
# 
#         This works with some clever shifting and AND / OR operations.
#         Data is LOADED as 8-bit, then promoted to 32-bits:
#         /ABCD EFGH/ (8 bits of data)
#         /0000 0000/0000 0000/0000 0000/ABCD EFGH/ (8 bits of data as a 32-bit word)
# 
#         Once promoted, we can do some shifting, AND and OR operations:
#         /0000 0000/0000 ABCD/EFGH 0000/0000 0000/ (shifted << 12)
#         /0000 0000/0000 ABCD/EFGH 0000/ABCD EFGH/ (bitwise OR of previous two lines)
#         /0000 0000/0000 ABCD/0000 0000/0000 EFGH/ (bitwise AND with mask 0xF000F)
#         /0000 00AB/CD00 0000/0000 00EF/GH00 0000/ (prev. line shifted << 6)
#         /0000 00AB/CD00 ABCD/0000 00EF/GH00 EFGH/ (bitwise OR of previous two lines)
#         /0000 00AB/0000 00CD/0000 00EF/0000 00GH/ (bitwise AND with 0x3030303)
# 
#         Then we change the view of the data to interpret it as 4x8 bit:
#         [000000AB, 000000CD, 000000EF, 000000GH]  (change view from 32-bit to 4x8-bit)
# 
#         The converted bits are then mapped to values in the range [-40, 40] according to a lookup chart.
#         The mapping is based on specifications in the breakthough docs:
#         https://github.com/UCBerkeleySETI/breakthrough/blob/master/doc/RAW-File-Format.md
# 
#     """
#     two_eight_lookup = {0: 40,
#                         1: 12,
#                         2: -12,
#                         3: -40}
# 
#     tmp = data.astype(np.uint32)
#     tmp = (tmp | (tmp << 12)) & 0xF000F
#     tmp = (tmp | (tmp << 6)) & 0x3030303
#     tmp = tmp.byteswap()
#     tmp = tmp.view('uint8')
#     mapped = np.array(tmp, dtype=np.int8)
#     for k, v in two_eight_lookup.items():
#         mapped[tmp == k] = v
#     return mapped
# 
# def unpack_4to8(data):
#     """ Promote 2-bit unisgned data into 8-bit unsigned data.
# 
#     Args:
#         data: Numpy array with dtype == uint8
# 
#     Notes:
#         # The process is this:
#         # ABCDEFGH [Bits of one 4+4-bit value]
#         # 00000000ABCDEFGH [astype(uint16)]
#         # 0000ABCDEFGH0000 [<< 4]
#         # 0000ABCDXXXXEFGH [bitwise 'or' of previous two lines]
#         # 0000111100001111 [0x0F0F]
#         # 0000ABCD0000EFGH [bitwise 'and' of previous two lines]
#         # ABCD0000EFGH0000 [<< 4]
#         # which effectively pads the two 4-bit values with zeros on the right
#         # Note: This technique assumes LSB-first ordering
#     """
# 
#     tmpdata = data.astype(np.int16)  # np.empty(upshape, dtype=np.int16)
#     tmpdata = (tmpdata | (tmpdata << 4)) & 0x0F0F
#     # tmpdata = tmpdata << 4 # Shift into high bits to avoid needing to sign extend
#     updata = tmpdata.byteswap()
#     return updata.view(data.dtype)
# 
# def unpack(data, nbit):
#     """upgrade data from nbits to 8bits
# 
#     Notes: Pretty sure this function is a little broken!
#     """
#     if nbit > 8:
#         raise ValueError("unpack: nbit must be <= 8")
#     if 8 % nbit != 0:
#         raise ValueError("unpack: nbit must divide into 8")
#     if data.dtype not in (np.uint8, np.int8):
#         raise TypeError("unpack: dtype must be 8-bit")
#     if nbit == 8:
#         return data
#     if nbit == 4:
#         data = unpack_4to8(data)
#         return data
#     if nbit == 2:
#         data = unpack_2to8(data)
#         return data
#     if nbit == 1:
#         data = unpack_1to8(data)
#         return data
# 
#     return None ## SHOULD NEVER HAPPEN


# Path: tests/test_unpack.py
from blimpy.utils import unpack_1to8, unpack_2to8, unpack_4to8, unpack
import numpy as np
import pytest

    a = np.array([0b00011011, 0b11100100], dtype=np.uint8)
    b = np.array([40, 12, -12, -40, -40, -12, 12, 40], dtype=np.int8)

    c = unpack_2to8(a)

    assert np.allclose(b, c)

def test_4to8():
    # Create an array that should come out as [0, 1, 2, 3, 3, 2, 1, 0]
    # In binary, this is [00, 01, 10, 11, 11, 10, 01, 00]
    # Convert to 8-bit, this is [0b00011011, 0b11100100]

    # Test 4-bit unpack
    a = np.array([0b00000001, 0b00100011], dtype=np.uint8)
    b = np.array([0, 1, 2, 3], dtype=np.uint8)
    c = unpack_4to8(a)
    assert np.allclose(b, c)

def test_unpack():

    # Test 2-bit unpack
    a = np.array([0b00011011, 0b11100100], dtype=np.uint8)
    b = np.array([40, 12, -12, -40, -40, -12, 12, 40], dtype=np.int8)
    c = unpack(a, 2)
    assert np.allclose(b, c)

    # Catch exceptions
    with pytest.raises(ValueError):
        unpack(a, 16)  # nbit <= 8 is reqd
    with pytest.raises(ValueError):

        unpack(a, 3)  # nbit must divide 8 (1,2,4 or 8)