class TreeNode:
    def __init__(self, key, val, left=None, right=None, parent=None):
        self.key = key
        self.payload = val
        self.leftChild = left
        self.rightChild = right
        self.parent = parent
        self.balanceFactor = 0

    def hasLeftChild(self):
        return self.leftChild

    def hasRightChild(self):
        return self.rightChild

    def isRoot(self):
        return not self.parent

    def isLeaf(self):
        return not (self.rightChild or self.isLeftChild)

    def isLeftChild(self):
        return self.parent and self.parent.leftChild == self

    def isRightChild(self):
        return self.parent and self.parent.rightChild == self

    def hasAnyChildren(self):
        return self.rightChild or self.leftChild

    def hasBothChildren(self):
        return self.rightChild and self.leftChild

    def replaceNodeData(self, key, value, lc, rc):
        self.key = key
        self.payload = value
        self.leftChild = lc
        self.rightChild = rc
        if self.hasLeftChild():
            self.leftChild.parent = self
        if self.hasRightChild():
            self.rightChild.parent = self

    def __iter__(self):
        if self:
            if self.hasLeftChild():
                """ 下面相当于做了递归调用
                self.leftChild.__iter__()
                """
                for elem in self.leftChild:
                    yield elem
            yield self.key
            if self.hasRightChild():
                for elem in self.rightChild:
                    yield elem

    def findSuccessor(self):
        succ = None
        if self.hasRightChild():
            succ = self.rightChild.findMin() #找右子树的最小节点
        else:
            if self.parent:
                if self.isLeftChild():
                    succ = self.parent
                else:
                    self.parent.righttChild = None
                    succ = self.parent.findSuccessor()
                    self.parent.rightChild = self
        return succ

    def findMin(self):
        current = self
        while current.hasLeftChild():
            current = current.leftChild
        return current

    def spliceOut(self): #把找到的继承节点摘出来
        if self.isLeaf(): #如果是叶节点
            if self.isLeftChild():
                self.parent.leftChild = None
            else:
                self.parent.rightChild = None
        elif self.hasAnyChildren(): #
            if self.hasLeftChild():
                if self.isLeftChild():
                    self.parent.leftChild = self.leftChild
                else:
                    self.parent.rightChild = self.leftChild
                self.leftChild.parent = self.parent
            else:
                if self.isLeftChild():
                    self.parent.leftChild = self.rightChild
                else:
                    self.parent.rightChild = self.rightChild
                self.rightChild.parent = self.parent

"""
================================================================
"""

class BinarySearchTree:
    def __init__(self):
        self.root = None
        self.size = 0

    def length(self):
        return self.size

    def __len__(self):
        return self.size

    def __iter__(self):
        return self.root.__iter__()

    def __setitem__(self, k, v):
        self.put(k, v)

    def put(self, key, val):
        if self.root:
            self._put(key, val, self.root)
        else:
            self.root = TreeNode(key, val)
        self.size = self.size + 1

    def _put(self, key, val, currentNode):
        if key < currentNode.key:
            if currentNode.hasLeftChild():
                self._put(key, val, currentNode.leftChild)
            else:
                currentNode.leftChild = TreeNode(key, val, parent=currentNode)
        else:
            if currentNode.hasRightChild():
                self._put(key, val, currentNode.rightChild)
            else:
                currentNode.rightChild = TreeNode(key, val, parent=currentNode)

    def get(self, key):
        if self.root:
            res = self._get(key, self.root)
            if res:
                return res.payload
            else:
                return None

    def _get(self, key, currentNode):
        if not currentNode:
            return None
        elif currentNode.key == key:
            return currentNode
        elif key < currentNode.key:
            return self._get(key, currentNode.LeftChild)
        else:
            return self._get(key, currentNode.rightChild)

    def __getitem__(self, key):
        return self.get(key)

    def __contains__(self, key):
        if self._get(key, self.root):
            return True
        else:
            return False

    def delete(self, key):
        if self.size > 1:
            nodeToRemove = self._get(key, self.root)
            if nodeToRemove:
                self.remove(nodeToRemove)
                self.size = self.size - 1
            else:
                raise KeyError('Error, key not in tree')
        elif self.size == 1 and self.root.key == key:
            self.root = None
            self.size = 0
        else:
            raise KeyError('Error, key not in tree')

    def __delitem__(self, key):
        self.delete(key)

    def remove(self, currentNode):
        if currentNode.isLeaf(): #被删节点没有子节点
            if currentNode == currentNode.parent.leftChild:
                currentNode.parent.leftChild = None
            else:
                currentNode.parent.rightChild = None

        elif currentNode.hasBothChildren(): #被删节点有两个子节点
            succ = currentNode.findSuccessor() #寻找继承节点
            succ.spliceOut() #把继承节点的相关节点断开重连
            currentNode.key = succ.key
            currentNode.payload = succ.payload

        else: #被删节点有一个子节点
            if currentNode.hasLeftChild: #被删节点的子节点在左侧
                if currentNode.isLeftChild(): #被删节点是某个节点的左节点
                    currentNode.leftChild.parent = currentNode.parent
                    currentNode.parent.leftChild = currentNode.leftChild
                elif currentNode.isRightChild: #被删节点是某个节点的右节点
                    currentNode.rightChild.parent = currentNode.parent
                    currentNode.parent.rightChild = currentNode.rightChild
                else: #被删节点是根节点，把左子节点升为根节点
                    currentNode.replaceNodeData(currentNode.leftChild.key,
                    currentNode.leftChild.payload,
                    currentNode.leftChild.leftChild,
                    currentNode.leftChild.rightChild)
            else: #被删节点的子节点在右侧
                if currentNode.isLeftChild(): #被删节点是某个节点的左节点
                    currentNode.rightChild.parent = currentNode.parent
                    currentNode.parent.leftChild = currentNode.rightChild
                elif currentNode.isRightChild: #被删节点是某个节点的右节点
                    currentNode.rightChild.parent = currentNode.parent
                    currentNode.parent.rightChild = currentNode.rightChild
                else: #被删节点是根节点，把右子节点升为根节点
                    currentNode.replaceNodeData(currentNode.rightChild.key,
                    currentNode.righttChild.payload,
                    currentNode.rightChild.leftChild,
                    currentNode.rightChild.rightChild)

"""
====================================================================
"""

class AVLTree(BinarySearchTree):
    def _put(self, key, val, currentNode):
        if key < currentNode.key:
            if currentNode.hasLeftChild():
                self._put(key, val, currentNode.leftChild)
            else:
                currentNode.leftChild = TreeNode(key, val, parent=currentNode)
                self.updateBalance(currentNode.leftChild)
        else:
            if currentNode.hasRightChild():
                self._put(key, val, currentNode.rightChild)
            else:
                currentNode.rightChild = TreeNode(key, val, parent=currentNode)
                self.updateBalance(currentNode.rightChild)

    def updateBalance(self, node):
        if node.balanceFactor > 1 or node.balanceFactor < -1:
            self.rebalance(node)
            return
        if node.parent != None:
            if node.isLeftChild():
                node.parent.balanceFactor += 1
            elif node.isRightChild():
                node.parent.balanceFactor -= 1
            if node.parent.balanceFactor != 0: #平衡后父节点平衡因子不为0，说明平衡导致了左/右子树的高度发生了变化
                self.updateBalance(node.parent) #递归向上调整父节点

    def rebalance(self, node):
        if node.balanceFactor < 0:
            if node.rightChild.balanceFactor > 0: #节点右重，子节点左重时
                self.rotateRight(node.rightChild) #先右旋子节点
                self.rotateLeft(node) #再左旋节点
            else: #节点右重，子节点没有左重
                self.rotateLeft(node)
        elif node.balanceFactor > 0:
            if node.leftChild.balanceFactor < 0:
                self.rotateLeft(node.leftChild)
                self.rotateRight(node)
            else:
                self.rotateRight(node)

    def rotateLeft(self, rotRoot): #左旋，旧节点命名为rotRoot
        newRoot = rotRoot.rightChild
        rotRoot.rightChild = newRoot.leftChild #新根左节点放到旧根右节点
        if newRoot.leftChild != None:
            newRoot.leftChild.parent = rotRoot
        newRoot.parent = rotRoot.parent
        if rotRoot.isRoot(): #如果旧节点是根节点
            self.root = newRoot #将新节点设为根节点
        else:
            if rotRoot.isLeftChild():
                rotRoot.parent.leftChild = newRoot
            else:
                rotRoot.parent.rightChild = newRoot
        newRoot.leftChild = rotRoot #旧根节点设为新根节点的左子节点
        rotRoot.parent = newRoot
        rotRoot.balanceFactor = rotRoot.balanceFactor + \
            1 - min(newRoot.balanceFactor, 0)
        newRoot.balanceFactor = newRoot.balanceFactor + \
            1 + max(rotRoot.balanceFactor, 0)

    def rotateRight(self, rotRoot): #右旋，旧节点命名为rotRoot
        newRoot = rotRoot.leftChild
        rotRoot.rightChild = newRoot.rightChild #新根右节点放到旧根左节点
        if newRoot.rightChild != None:
            newRoot.rightChild.parent = rotRoot
        newRoot.parent = rotRoot.parent
        if rotRoot.isRoot(): #如果旧节点是根节点
            self.root = newRoot #将新节点设为根节点
        else:
            if rotRoot.isLeftChild():
                rotRoot.parent.leftChild = newRoot
            else:
                rotRoot.parent.rightChild = newRoot
        newRoot.rightChild = rotRoot #旧根节点设为新根节点的左子节点
        rotRoot.parent = newRoot
        rotRoot.balanceFactor = rotRoot.balanceFactor - \
            1 + min(newRoot.balanceFactor, 0)
        newRoot.balanceFactor = newRoot.balanceFactor - \
            1 - max(rotRoot.balanceFactor, 0)


__author__ = 'NM242826'

import matplotlib.pyplot as plt
import numpy as np
plt.ion()
n = 100
x = range(n)
y = [np.sin(i) for i in x]

fig, ax = plt.subplots()
ax.set_xlim((0,n))

for k in range(n):
    plt.plot(x[0:k], y[0:k], 'g-', linewidth=1.5, markersize=4)
    ax.set_xlim((0, k))
    plt.pause(0.01)

plt.show(block=True)


"""
Usage: python seam_carving.py <rs> <r/c> <scale> <image_in> <image_out> <energy_map_type> <y/n>
Or:python seam_carving.py <rm> <image_in> <image_out> <image_mask>
<rm/rs> rm represents remove rs represents resize
<r/c> 删除列还是删除行
<scale> 缩小为原来的多少(0,1)之间的浮点数
<energy_map_type> c:calc_energy b:backward_energy f:forward_energy
<y/n> y:使用grabCut n:不使用grabCut
"""
import sys
from scipy.ndimage import convolve
import cv2
import numba
import numpy as np
from tqdm import trange
from scipy import ndimage as ndi

ENERGY_MASK_CONST = 100000.0              # large energy value for protective masking
MASK_THRESHOLD = 10                       # minimum pixel intensity for binary mask
SEAM_COLOR = np.array([255, 200, 200])

def visualize(im, seams, rotate=False):
    vis = im.astype(np.uint8)
    r, c, _ = vis.shape
    if seams is not None:
        for i in range(r):
            vis[i][seams[i]] = SEAM_COLOR
    if rotate:
        vis = np.rot90(vis, 3, (0, 1))
    #cv2.namedWindow('visualization', cv2.WINDOW_NORMAL)
    #cv2.resizeWindow("visualization", 500, 500)
    cv2.imshow("visualization", vis)
    cv2.waitKey(30)
    return vis

@numba.jit()
def cumulative_energy(energy):
    r, c = energy.shape
    backtrack = np.zeros((r, c), dtype=np.int64)
    energy_map = energy.copy()
    energy_map[0] = energy[0]

    for i in range(1, r):
        for j in range(0, c):
            # 处理左侧边缘
            if j == 0:
                idx = np.argmin(energy_map[i - 1, j : j + 2])
                backtrack[i, j] = idx
                min_energy = energy_map[i - 1, idx + j]
            # 处理右侧边缘
            elif j == c - 1:
                idx = np.argmin(energy_map[i - 1, j - 1 : j + 1])
                backtrack[i, j] = idx - 1  # idx + j - 1
                min_energy = energy_map[i - 1, idx + j - 1]
            else:
                idx = np.argmin(energy_map[i - 1, j - 1 : j + 2])
                backtrack[i, j] = idx - 1  # idx+j-1
                min_energy = energy_map[i - 1, idx + j - 1]
            energy_map[i, j] += min_energy

    return backtrack, energy_map

def calc_energy(img):
    # np.array函数创建了一个类似矩阵的东西
    # 一个二维数组
    filter_x = np.array(
        [
            [1.0, 2.0, 1.0],
            [0.0, 0.0, 0.0],
            [-1.0, -2.0, -1.0],
        ]
    )
    # stack函数,我们要对红绿蓝都计算
    filter_x = np.stack([filter_x] * 3, axis=2)

    filter_y = np.array(
        [
            [1.0, 0.0, -1.0],
            [2.0, 0.0, -2.0],
            [1.0, 0.0, -1.0],
        ]
    )
    filter_y = np.stack([filter_y] * 3, axis=2)

    # 类型转化，转化为float
    img = img.astype("float32")

    # 计算卷积和
    convolved = np.absolute(convolve(img, filter_x)) + np.absolute(
        convolve(img, filter_y)
    )
    # 将红绿蓝通道中的能量相加
    energy_map = convolved.sum(axis=2)

    return energy_map

def backward_energy(im):
    xgrad = ndi.convolve1d(im, np.array([1, 0, -1]), axis=1, mode="wrap")
    ygrad = ndi.convolve1d(im, np.array([1, 0, -1]), axis=0, mode="wrap")
    grad_mag = np.sqrt(np.sum(xgrad**2, axis=2) + np.sum(ygrad**2, axis=2))

    return grad_mag

def forward_energy(im):
    """
    Forward energy algorithm as described in "Improved Seam Carving for Video Retargeting"
    by Rubinstein, Shamir, Avidan.
    Vectorized code adapted from
    https://github.com/axu2/improved-seam-carving.
    """
    h, w = im.shape[:2]
    im = cv2.cvtColor(im.astype(np.uint8), cv2.COLOR_BGR2GRAY).astype(np.float64)

    energy = np.zeros((h, w))
    m = np.zeros((h, w))

    U = np.roll(im, 1, axis=0)
    L = np.roll(im, 1, axis=1)
    R = np.roll(im, -1, axis=1)

    cU = np.abs(R - L)
    cL = np.abs(U - L) + cU
    cR = np.abs(U - R) + cU

    for i in range(1, h):
        mU = m[i - 1]
        mL = np.roll(mU, 1)
        mR = np.roll(mU, -1)
        mULR = np.array([mU, mL, mR])
        cULR = np.array([cU[i], cL[i], cR[i]])
        mULR += cULR
        argmins = np.argmin(mULR, axis=0)
        m[i] = np.choose(argmins, mULR)
        energy[i] = np.choose(argmins, cULR)

    return energy

def crop_c(img, scale_c, energy_map_type, if_grabCut, rotated):
    r, c, _ = img.shape
    new_c = int((1 - scale_c) * c)
    img = resize_image(img, new_c, energy_map_type, if_grabCut, rotated)
    return img

def crop_r(img, scale_r, energy_map_type, if_grabCut):
    img = np.rot90(img, 1, (0, 1))
    img = crop_c(img, scale_r, energy_map_type, if_grabCut, True)
    img = np.rot90(img, 3, (0, 1))
    return img

def add_c(img, scale_c, energy_map_type, if_grabCut, rotated):
    r, c, _ = img.shape
    new_c = int(scale_c * c)
    img = add_column(img, new_c - c, energy_map_type, if_grabCut, rotated)
    return img

def add_r(img, scale_r, energy_map_type, if_grabCut):
    img = np.rot90(img, 1, (0, 1))
    img = add_c(img, scale_r, energy_map_type, if_grabCut, True)
    img = np.rot90(img, 3, (0, 1))
    return img

def find_seam(paths, end_x):
    r, c = paths.shape[:2]
    seam = [end_x]
    for i in range(r - 1, 0, -1):
        cur_x = seam[-1]
        offset_of_prev_x = paths[i][cur_x]
        seam.append(cur_x + offset_of_prev_x)
    seam.reverse()

    return seam

def remove_seam(img, seam):
    r, c = img.shape[:2]
    img_removed = np.zeros((r, c - 1, 3))
    for r2 in range(r):
        img_removed[r2] = np.delete(img[r2], seam[r2], axis=0)
    return img_removed

def remove_seam_grayscale(img ,seam):
    r, c = img.shape[:2]
    img_removed = np.zeros((r, c - 1))
    for i in range(r):
        img_removed[i]=np.delete(img[i], seam[i], axis=0)
    return img_removed

def add_seam(im, seam_idx, rotated=False):
    h, w = im.shape[:2]
    visualize(im, seam_idx, rotated)
    output = np.zeros((h, w + 1, 3))

    for row in range(h):
        col = seam_idx[row]
        for ch in range(3):
            if col == 0:
                p = np.average(im[row, col : col + 2, ch])
                output[row, col, ch] = im[row, col, ch]
                output[row, col + 1, ch] = p
                output[row, col + 1 :, ch] = im[row, col:, ch]
            else:
                p = np.average(im[row, col - 1 : col + 1, ch])
                output[row, :col, ch] = im[row, :col, ch]
                output[row, col, ch] = p
                output[row, col + 1 :, ch] = im[row, col:, ch]

    return output

def add_column(img, add_c, energy_map_type, if_grabCut, rotated=False):
    img_original = img.copy()
    r, c, channels = img.shape
    seams_record = np.ones((add_c, r), dtype=int)

    if(if_grabCut):
        energy_grabCut=improve_seam(img,rotated)

    for i in trange(add_c, desc="cropping image by {0} pixels".format(add_c)):
        if energy_map_type == "c":
            energy_map = calc_energy(img)
        elif energy_map_type == "b":
            energy_map = backward_energy(img)
        else:
            energy_map = forward_energy(img)

        if(if_grabCut):
            energy_map+=energy_grabCut

        backtrack, energy_totals = cumulative_energy(energy_map)
        energy_minc = list(energy_totals[-1]).index(min(energy_totals[-1]))
        seam = find_seam(backtrack, energy_minc)
        visualize(img, seam, rotated)
        seams_record[i] = seam
        img = remove_seam(img, seam)
        if(if_grabCut):
            energy_grabCut=remove_seam_grayscale(energy_grabCut,seam)

    for i in trange(add_c, desc="expanding image by {0} pixels".format(add_c)):
        img_original = add_seam(img_original, seams_record[i], rotated)
        for i2 in range(i + 1, add_c):
            seams_record[i2][np.where(seams_record[i2] >= seams_record[i])] += 2

    return img_original

def resize_image(
    full_img,
    cropped_c,
    energy_map_type,
    if_grabCut,
    rotated=False,
):
    img = full_img.copy()
    if(if_grabCut):
        energy_grabCut=improve_seam(img,rotated)

    for _ in trange(cropped_c, desc="cropping image by {0} pixels".format(cropped_c)):

        if energy_map_type == "c":
            energy_map = calc_energy(img)
        elif energy_map_type == "b":
            energy_map = backward_energy(img)
        else:
            energy_map = forward_energy(img)

        if(if_grabCut):
            energy_map+=energy_grabCut

        backtrack, energy_totals = cumulative_energy(energy_map)
        energy_minc = list(energy_totals[-1]).index(min(energy_totals[-1]))
        seam = find_seam(backtrack, energy_minc)
        visualize(img, seam, rotated)
        img = remove_seam(img, seam)
        if(if_grabCut):
            energy_grabCut=remove_seam_grayscale(energy_grabCut,seam)

    return img

def improve_seam(im_rgb, rotated):
    im_cut = im_rgb.copy().astype('uint8')
    if rotated:
        im_cut_rot = np.rot90(im_cut, 3, (0, 1))
    else:
        im_cut_rot = im_cut.copy()
    r0, c0 = im_cut.shape[:2]
    r = cv2.selectROI('input', im_cut_rot, False)
    rect = (int(r[1]), int(r0-r[0]-r[2]), int(r[3]), int(r[2]))
    rows, cols = im_cut.shape[0], im_cut.shape[1]
    mask = np.zeros(im_cut.shape[:2],np.uint8)
    bgdModel = np.zeros((1,65),np.float64)
    fgdModel = np.zeros((1,65),np.float64)
    cv2.grabCut(im_cut,mask,rect,bgdModel,fgdModel,10,cv2.GC_INIT_WITH_RECT)
    mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')#0和2做背景
    energy_map =calc_energy(im_rgb)
    for i in range(rows):
            for j in range(cols):
                if (mask2[i][j] == 1):
                    energy_map[i][j] = 10000
                elif ( mask2[i][j]==3):
                    energy_map[i][j] = 10000
                else:
                    energy_map[i][j] = 0
    return energy_map

def object_removal(img,mask):
    h, w = img.shape[:2]
    output = img

    while len(np.where(mask>MASK_THRESHOLD)[0])> 0 :
        energy_map=calc_energy(img)
        #在能量图中导入MASK的影响
        energy_map[np.where(mask>MASK_THRESHOLD)]= -ENERGY_MASK_CONST
        backtrack, energy_totals = cumulative_energy(energy_map)
        energy_minc = list(energy_totals[-1]).index(min(energy_totals[-1]))
        seam = find_seam(backtrack, energy_minc)
        visualize(img, seam)
        img = remove_seam(img, seam)
        mask=remove_seam_grayscale(mask,seam)
    num_add=int(w-img.shape[1])
    output=add_column(img,num_add,'c')
    return output

def main():
    if len(sys.argv) == 5:
        in_filename = sys.argv[2]
        out_filename = sys.argv[3]
        mask_filename = sys.argv[4]
        img_in = cv2.imread(in_filename).astype(np.float64)
        #read in protect mask in gray scale
        img_mask = cv2.imread(mask_filename,0).astype(np.float64)
        img_out=object_removal(img_in,img_mask)
        cv2.imwrite(out_filename, img_out.astype(np.float64))
    elif len(sys.argv) == 8:

        which_axis = sys.argv[2]
        scale = float(sys.argv[3])
        in_filename = sys.argv[4]
        out_filename = sys.argv[5]
        energy_map_type = sys.argv[6]
        if_grabCut =sys.argv[7]

        img = cv2.imread(in_filename).astype(np.float64)
        if(if_grabCut)=='y':
            have_grabCut=True
        else:
            have_grabCut=False

        if scale < 1:
            if which_axis == "r":
                out = crop_r(img, scale, energy_map_type, have_grabCut)
            elif which_axis == "c":
                out = crop_c(img, scale, energy_map_type, have_grabCut, False)
            else:
                print(
                    "Usage: python seam_carving.py <rm/rs> <r/c> <scale> <image_in> <image_out> <energy_map_type> <y/n>",
                    file=sys.stderr,
                )
                sys.exit(1)

        elif scale > 1:
            if which_axis == "r":
                out = add_r(img, scale, energy_map_type, have_grabCut)
            elif which_axis == "c":
                out = add_c(img, scale, energy_map_type, have_grabCut, False)
            else:
                print(
                    "Usage: python seam_carving.py <rm/rs> <r/c> <scale> <image_in> <image_out> <energy_map_type> <y/n>",
                    file=sys.stderr,
                )
                sys.exit(1)

        out = out.astype(np.float64)
        cv2.imwrite(out_filename, out.astype(np.float64))
    else:
        print(
            "Usage: python seam_carving.py <rm/rs> <r/c> <scale> <image_in> <image_out> <energy_map_type> <y/n>",
            file=sys.stderr,
        )
        sys.exit(1)

if __name__ == "__main__":
    main()


def square(a):
    return a * a

a = 1

squareNums = {}

minNum = 0

sum = 0

while True:
    squareNums[square(a)] = 1
    a += 1
    if square(a) > 10000:
        break

m = int(input())
n = int(input())

for i in range(m, n + 1):
    if i in squareNums:
        if minNum == 0:
            minNum = i
        sum += i

if sum == 0:
    print(-1)
else:
    print(sum)
    print(minNum)


import gradio as gr
from dotenv import load_dotenv
from components.reader.reader import reader
from components.adapter.adapter import adapter
from components.embedder.embedder import embedder
from components.vector_database.vector_database import vector_database
from components.llm.llm import llm
import pandas as pd
import time
import random
import os

outputs = {}

load_dotenv()
SERVER_URL = os.getenv("QUERY_URL")
PORT = int(os.getenv("QUERY_PORT"))

EMBEDDER = "all-MiniLM-L6-v2"
VECTOR_DB = "faiss"
MODEL_NAME = "GPT4All"

LLM = llm(model_name=MODEL_NAME)  # temporary task given

def shorten_list(lst):
    if not lst:
        return "[]"

    first_four_elements = str(lst[:5])
    last_element = lst[-1]

    shortened_list = first_four_elements[
        : len(first_four_elements) - 1
    ]  # will remove the ] from the end
    shortened_list += 3 * "."
    shortened_list += str(last_element)
    shortened_list += "]"

    return shortened_list

def init_pipeline(MODEL_NAME, TASK):
    global embedder1
    global vector_database1

    embedder1 = embedder(embedding_model=EMBEDDER)
    vector_database1 = vector_database(name=VECTOR_DB)

    return [gr.Button.update(visible=True), gr.Textbox.update(visible=True)]

def llm_flow(MODEL_NAME, TASK, QUERY):
    response = LLM.query(query=QUERY, chunk_list=outputs["chunk_list"])
    print(response)
    return response

def embedder_flow(QUERY):
    query_embedding_vector = embedder1.embed_query(QUERY)
    outputs["query_embedding_vector"] = query_embedding_vector

    data = {"query": [QUERY], "embedding": [shorten_list(query_embedding_vector)]}
    df = pd.DataFrame(data)
    return df

def vector_db_flow(CHUNK_COUNT):
    time.sleep(1.5)
    chunk_list = vector_database1.search_query(
        outputs["query_embedding_vector"], int(CHUNK_COUNT)
    )
    outputs["chunk_list"] = chunk_list

    data = {
        "Relevant Chunks": chunk_list,
        "Relevance Rank": list(range(1, len(chunk_list) + 1)),
    }
    df = pd.DataFrame(data)
    return df

def trigger_embedder():
    return str(random.randint(1, 1000))

def trigger_vector_database():
    return str(random.randint(1, 1000))

def trigger_llm():
    return str(random.randint(1, 1000))

# Issues:
# - the triggers
# - the rerun bug

with gr.Blocks() as demo:
    with gr.Row():
        with gr.Column():
            embedder_label = gr.Label(value="Embedder")
            embedder_output = gr.Dataframe(
                wrap=True,
                interactive=False,
                headers=["Query", "Embedding"],
                datatype=["str", "number"],
                label="Embedder Output",
            )

            vector_database_label = gr.Label(value="Vector Database")
            vector_database_output = gr.Dataframe(
                wrap=True,
                height=10000,
                interactive=False,
                headers=["Chunks", "Relevance Rank"],
                datatype=["str"],
                label="Vector Database Output",
            )

            llm_response_output = gr.Textbox(interactive=False, label="LLM Response")

            llm_trigger = gr.Textbox(visible=False)
            embedder_trigger = gr.Textbox(visible=False)
            vector_database_trigger = gr.Textbox(visible=False)

        with gr.Column():
            llm_model_input = gr.Dropdown(
                label="Large Language Model", choices=["GPT4All"], value="GPT4All"
            )
            task_input = gr.Dropdown(
                label="Task", choices=["Question Answering"], value="Question Answering"
            )
            chunk_count_input = gr.Number(
                interactive=True, label="Top Rank Count", value=1
            )
            pipeline_btn = gr.Button(value="Init Pipeline")

            query_input = gr.Textbox(visible=False, interactive=True, label="Query")
            btn = gr.Button(visible=False, value="Run")

            btn.click(fn=trigger_embedder, inputs=[], outputs=[embedder_trigger])
            embedder_trigger.change(
                fn=embedder_flow, inputs=[query_input], outputs=[embedder_output]
            )

            embedder_output.change(
                fn=trigger_vector_database, inputs=[], outputs=[vector_database_trigger]
            )
            vector_database_trigger.change(
                fn=vector_db_flow,
                inputs=[chunk_count_input],
                outputs=[vector_database_output],
            )

            vector_database_output.change(
                fn=trigger_llm, inputs=[], outputs=[llm_trigger]
            )
            llm_trigger.change(
                fn=llm_flow,
                inputs=[llm_model_input, task_input, query_input],
                outputs=[llm_response_output],
            )
            pipeline_btn.click(
                fn=init_pipeline,
                inputs=[llm_model_input, task_input],
                outputs=[btn, query_input],
            )

if __name__ == "__main__":
    demo.launch(share = False, debug = True, server_name = SERVER_URL, server_port = PORT)


import time
import re
import logging
import scipy.misc
from scipy.ndimage.filters import gaussian_filter
from fpga import api
import numpy as np
import cv2
import os,sys
from bitstring import BitArray
from PIL import Image
import numpngw

logging.basicConfig(format='%(levelname)-6s[%(filename)s:%(lineno)d] %(message)s'
                    ,level=logging.DEBUG)

class T6(api):

    address = {'mk_routine_rst': 0x00,
               'wire_check_set': 0x01,
                     'param_en': 0x02,
                     'param_in': 0x03,
                   'param_addr': 0x04,
                   'rowmap_set': 0x0A,
                     'chip_rst': 0x10,
                 'wire_check_o': 0x20,
                     'fifo_cnt': 0x21,
                    'param_out': 0x22,
                     'exposure': 0x11,
                      'pat_num': 0x12,
                  'mem_pat_num': 0x14,
                    'sys_start': 0x40,
                     'mem_mode': 0x13,
                      'trigger': 0x53,
                    'fifo_flag': 0x6a,
                       'mem_wr': 0x80,
                      'ldo_spi': 0x81,
                      'cis_spi': 0x82,
                       'mem_rd': 0xA0,
                        'image': 0xB0,
                      'adc2_rd': 0xA3
               }

    param = {      'numPattern' : 0,
                    'Texp_ctrl' : 1,
                    'Tdes2_d' : 2,
                    'Tdes2_w' : 3,
                    'Tmsken_d' : 4,
                    'Tmsken_w' : 5,
                    'Tgsub_w' : 6,
                    'Tgl_Res' : 7,
                    'Tproj_dly' : 8,
                    'Tadd' : 9,
                    'NumRep' : 10,
                    'NumGsub' : 11,
                    'TExpRST' : 12,
                    'Tdrain_w' : 13,
                    'imgCountTrig' : 14,
                    'TdrainR_d' : 15,
                    'TdrainF_d' : 16,
                    'TLedOn' : 17,
                    'Select' : 18,
                 'CAM_TG_pulseWid' : 19,
                 'CAM_TG_HoldTime' : 20,
                    'CAM_TG_delay' : 21,
                       'MASK_SIZE' : 22,
                        'IMG_SIZE' : 23,
                     'T_DEC_SEL_0' : 24,
                     'T_DEC_SEL_1' : 25,
                      'T_DEC_EN_0' : 26,
                      'T_DEC_EN_1' : 27,
                        'T_DONE_1' : 28,
                  'MSTREAM_Select' : 29,
                      'MU_NUM_ROW' : 30,
                    'SUB_IMG_SIZE' : 31,
                    'ADC1_Tcolumn' : 32,
                         'ADC1_T1' : 33,
                       'ADC1_T2_1' : 34,
                       'ADC1_T2_0' : 35,
                         'ADC1_T3' : 36,
                         'ADC1_T4' : 37,
                         'ADC1_T5' : 38,
                         'ADC1_T6' : 39,
                         'ADC1_T7' : 40,
                         'ADC1_T8' : 41,
                         'ADC1_T9' : 42,
                       'ADC1_TADC' : 43,
                    'ADC1_NUM_ROW' : 44,
                       'ADC1_Wait' : 45,
                       'T_MU_wait' : 46,
                    'ADC2_Tcolumn' : 47,
                         'ADC2_T1' : 48,
                       'ADC2_T2_1' : 49,
                       'ADC2_T2_0' : 50,
                         'ADC2_T3' : 51,
                         'ADC2_T4' : 52,
                         'ADC2_T5' : 53,
                         'ADC2_T6' : 54,
                         'ADC2_T7' : 55,
                         'ADC2_T8' : 56,
                         'ADC2_T9' : 57,
                       'ADC2_TADC' : 58,
                    'ADC2_NUM_ROW' : 59,
                       'ADC2_Wait' : 60,
                       'T_MU_wait' : 61,
                          'TrigNo' : 62,
                     'TrigOffTime' : 63,
                      'TrigOnTime' : 64,
                'UNIT_SUB_IMG_SIZE': 65,
                    'N_SUBREADOUTS': 66,
                      'spi_control': 67,
                         'i2c_data': 68,
                      'i2c_control': 69,
                   'ROWADD_INCRMNT': 70,
                     'TrigWaitTime': 71,
                           'TLEDNo': 72,
                          'TLEDOff': 73,
                           'TLEDOn': 74,
                         'TLEDWait': 75
            }

    # voltages for LDO
    voltages = np.array([3.3, 3.3, 3.3, 3.3,  # CS1
                         3.3, 3.3, 3.3, 3.3,  # CS2
                         3.3, 3.3, 3.3, 3.3]) # CS3

    # this is 96-bit on chip. To configure mask upload settings
    cis_spi_mu = [  '0100001000110000', '0000000000000000', # 95:80, 79:64
                    '0000000000000000', '0000000000000000', # 63:48, 47:32
                    '0000000000000000', '1000000000000000', # 31:16, 15:00
                    '0000000000000010', '0000000000000000']

    # this is only 48-bit on chip. To configure all other settings
    cis_spi_ro = [  '0000000000000000', '0000000000000000', # GARBJ, GARBJ
                    '0000000000000000', '0000000000011101', # GARBJ, 47:32
                    '1100000001111100', '1101111101000011', # 31:16, 15:00
                    '0000000000000001', '0000000000000000'] # <slave_select>

    col, row, tab, ch = 108, 320, 2, 3
    #frame_length = 552960
    # frame_length    = col*4*row*tab*2 # t6
    frame_length            = round((480*40*17*2*12*256/255) // 8) #t7
    unit_subframe_length    = round(480*40*17*1*1*256/255/8)

    adc2_container = bytearray(unit_subframe_length)
    adc1_container = bytearray(frame_length)

    BLOCK_SIZE      = 512

    # trying to fix importing t4 from path outside of Cam_API_py and still have it work
    # use module relative paths
    black_img_file  = os.path.join(os.path.dirname(__file__), 't6_black_image_avg.npy')
    bright_img_file = os.path.join(os.path.dirname(__file__), 't6_bright_image_avg.npy')
    gain_mat_file   = os.path.join(os.path.dirname(__file__), 'gain_mat.npy')
    gain_cali_file  = os.path.join(os.path.dirname(__file__), 't6_gain_cali.npy')
    img_map_file    = os.path.join(os.path.dirname(__file__), 't6_img_map.npy')
    img_graymap_file = os.path.join(os.path.dirname(__file__),'t6_img_graymap.npy')
    mask_map_file   = os.path.join(os.path.dirname(__file__), 't6_mask_map.npy')
    adc2_map_file   = os.path.join(os.path.dirname(__file__), 't7_adc2_map_1bit.npy')

    black_flag = False
    black_scale_flag = False
    gain_flag = False
    gain_scale_flag = False
    scale_flag = False
    MaskNumInMem=0
    MaskNumForCam=0
    MaskRepetition=0
    ldo_delay = 0.1

    def __init__(self, bitfile,reConfFPGA=True):

        super(T6, self).__init__(bitfile)
        logging.info("Bitfile: {}".format(bitfile))
        if(reConfFPGA):
            time.sleep(1)
            self.LDOConf(self.voltages)
            time.sleep(1)
            self.CISConf(self.cis_spi_mu)
            time.sleep(1)
            self.CISConf(self.cis_spi_ro)

            time.sleep(5)
            self.wire_in(self.address['mk_routine_rst'], 0x04)
            self.wire_in(self.address['mk_routine_rst'], 0x00)
            #self.wire_in(self.address['mask'], 0xfff)
            self.wire_in(self.address['chip_rst'], 0x01)
            #self.wire_in(self.address['chip_rst'], 0x00)
            self.wire_in(self.address['exposure'], 1)
            self.wire_in(self.address['pat_num'], 1)

            # self.param_set(self.param['imgCountTrig'],3000)
            self.param_set(self.param['TExpRST'], 10000) # 200MHz
            self.param_set(self.param['Tproj_dly'], 100)
            self.param_set(self.param['NumRep'], 1)
            self.param_set(self.param['NumGsub'], 0)
            self.param_set(self.param['Tdrain_w'], 0)

            self.param_set(self.param['Tgsub_w'], 100)
            self.param_set(self.param['TdrainF_d'], 20)
            self.param_set(self.param['TdrainR_d'], 10)

            #DELAY+WIDTH<=15
            self.param_set(self.param['Tmsken_d'], 4)
            self.param_set(self.param['Tmsken_w'], 11)

            self.param_set(self.param['Select'], 0x80000000)  # enable slave mode

        else:
            logging.info("Did not configure FPGA with bitfile")
            self.wire_in(self.address['mk_routine_rst'], 0x04)
            self.wire_in(self.address['mk_routine_rst'], 0x00)
            self.wire_in(self.address['chip_rst'], 0x01)

        self.mapping = self.ImgMapCal()
        # self.mapping = self.ImgGrayMapCal()

        self.mkmapping = self.MaskMapCal()
        if(os.path.exists(self.black_img_file)):
            self.black_img = np.load(self.black_img_file)
        if(os.path.exists(self.gain_cali_file)):
            self.gain_cali = np.load(self.gain_cali_file)
        if(os.path.exists(self.bright_img_file)):
            self.bright_img = np.load(self.bright_img_file)
            #self.coeff_mat = np.load(self.gain_cali_file)
            #self.coeff_mat[324:] = self.coeff_mat[0:324]
        if(os.path.exists(self.adc2_map_file)):
            self.adc2_map_1row = np.load(self.adc2_map_file).flatten()

###################################################

    def UploadMask(self, mask_file, MaskNum):
        """
        mask_file@in: mask file name and path
        MaskNum@in: give mask number which will be set in memory control
        """
        # reset camera
        self.wire_in(self.address['chip_rst'], 0x01)
        logging.info("Camera Stopped.")
        self.fmaskupload(mask_file)
        logging.info("Mask {} uploaded.".format(mask_file))
        self.wire_in(self.address['mem_pat_num'], MaskNum)
        self.MaskNumInMem=MaskNum
        logging.info("Mask #{}".format(MaskNum))

    def UploadMaskDummy(self, mask_file, MaskNum):
        """
    Dummy version of the mask upload which sets all variables except uploading mask to memory
        mask_file@in: mask file name and path
        MaskNum@in: give mask number which will be set in memory control
        """
        # reset camera
        self.wire_in(self.address['chip_rst'], 0x01)
        logging.info("Camera Stopped.")
    # self.fmaskupload(mask_file)
        # memory mask upload mode
        self.wire_in(self.address['mem_mode'], 0x01)
        self.wire_in(self.address['mk_routine_rst'], 0x04)
        self.wire_in(self.address['mk_routine_rst'], 0x01)
        logging.info("Mask {} was ***NOT*** uploaded.".format(mask_file))
        self.wire_in(self.address['mem_pat_num'], MaskNum)
        self.MaskNumInMem=MaskNum
        logging.info("Mask #{}".format(MaskNum))

    def readout_reset(self):
        self.wire_in(self.address['chip_rst'], 0x01)
        self.wire_in(self.address['chip_rst'], 0x00)

    def chip_reset(self):
        self.wire_in(self.address['mk_routine_rst'], 0x04)
        self.wire_in(self.address['mk_routine_rst'], 0x00)
        #self.wire_in(self.address['mask'], 0xfff)
        self.wire_in(self.address['chip_rst'], 0x01)

    def Pause(self):
        self.wire_in(self.address['chip_rst'], 0x01)
        logging.info("Camera Stopped.")

    def Resume(self):
        self.wire_in(self.address['chip_rst'], 0x00)
        logging.info("Camera Stopped.")

    def SetMaskParam(self, MaskNum, MaskRepNum=1):
        """
        MaskNum@in: set mask number applied in camera
        MaskRepNum@in: each mask repetition time in camera
        """
        # reset camera
        assert self.MaskNumInMem>0, "Please upload mask to camera first."
        assert self.MaskNumInMem%MaskNum==0, "CamMask #{} should dividable by MemMask #{}".format(MaskNum, self.MaskNumInMem)
        self.wire_in(self.address['pat_num'], MaskNum)
        logging.info("Camera mask #{}".format(MaskNum))
        self.MaskNumForCam=MaskNum
        self.param_set(self.param['NumRep'], MaskRepNum)
        logging.info("Camera mask repetition #{}".format(MaskRepNum))
        self.MaskRepetition=MaskRepNum

    def SetExposure(self, value):
        """
        value@in: exposure time in us
        """
        assert self.MaskNumForCam != 0, "Please set mask parameter to camera first."
        assert self.MaskRepetition != 0, "Please set mask parameter to camera first."
        logging.info("Setting Camera exposure #{}us ...".format(value))

        maskuploadtime = 26195 # 26195ns for maskupload, 200MHz freq. 5n
        SetAsClk = int((value*1000-maskuploadtime*self.MaskRepetition)/5-2)

        assert SetAsClk > 0, "Exposure time should larger than {}us".format((10+maskuploadtime*self.MaskRepetition)/1000)

        self.wire_in(self.address['exposure'], SetAsClk)
        logging.info("Camera exposure setting finished. #{} clk".format(SetAsClk))

    def getimg(self, mode='raw'):
        """
        mode = 'raw'/'black'
        """
        assert mode == 'raw' or 'black', "No {} mode. 'raw' or 'black'".format(mode)

        if mode == 'raw':
            self.showMode()
        if mode == 'black':
            self.showMode(black_scale=True)

        return self.arrange(self.imread())

    def nothing(self, x):
        pass

    def imshow(self, numbers = None):

        img_name = 'image'
        cv2.namedWindow(img_name)
        cv2.createTrackbar('Zoom', img_name, 10, 30, self.nothing)

        while True:

            factor = cv2.getTrackbarPos('Zoom',img_name)
            if factor == 0: factor = 1

            #img=self.arrange(self.imread())
            img=self.getimg()

            img_scaled = cv2.resize(img,None,fx=factor/10, fy=factor/10,
                                    interpolation = cv2.INTER_LINEAR)

            cv2.imshow(img_name, img_scaled)

            k=cv2.waitKey(1)
            if k==27: break

        cv2.destroyAllWindows()

    def setRowMap(self,rowmapGuide):
        logging.info("rewriting row map")
        set_write   = '0'+'{:015b}'+'{:016b}'
        done_write  = '1'+'{:015b}'+'{:016b}'
        totalRows   = len(rowmapGuide);      i=0;
        for current_map in rowmapGuide:
            from_row  = current_map[0]
            to_row    = current_map[1]
            write_value = int(set_write.format(from_row,to_row),2)
            done_value  = int(done_write.format(from_row,to_row),2)
            self.wire_in(self.address['rowmap_set'],write_value)
            time.sleep(1/1000)
            self.wire_in(self.address['rowmap_set'],done_value)
            time.sleep(1/1000)
            self.wire_in(self.address['rowmap_set'],write_value)
            time.sleep(1/1000)
            print("{}/{} done".format(i,totalRows),end='\r')
        self.wire_in(self.address['rowmap_set'],0)

#######################################################

    def showMode(self, black=False, black_scale=False, gain=False, gain_scale=False, scale=False):
        if black or black_scale and os.path.exists(self.black_img_file):
            if black_scale:
                self.black_scale_flag = True
            else:
                self.black_flag = True
            print("Black calliberation On")
        elif gain or gain_scale and os.path.exists(self.gain_cali_file):
            if gain_scale:
                self.gain_scale_flag = True
            else:
                self.gain_flag = True
            print("Black calliberation On")
            print("Gain calliberation On")
        elif scale:
            print("Normal scale On")
            self.scale_flag = True
        else:
            print("Raw image On")

    def LDOConf(self, voltages):
        '''
        Takes in voltage values used to set LDOs on board and configures
        byte stream to send to SPI_master module.
        ----------------------------------------------------------
        Variables

        SlaveSel:  1-hot encoded mask for chip select
        Addr:      8 bit pattern for DAC addr. Ordered as 0,0,0...A2,A1,A0
        Voltage:   8 bit pattern to control LDO voltage. Ordered from Msb -> Lsb
        Mode:      Currently unused in SPI_master. Can be used to change SPI mode later
                   for different CPOL and CPAH configurations.
        ------------------------------------------------------------
        Output to SPI_master

        words are formatted as { SS | Mode | Voltage | Addr } in python
        FPGA flips byte order so words recieved as { Addr | Voltage | Mode | SS } by SPI_master
        '''

        # SPI order as { SS | Mode | Voltage | Addr } per row
        SPI = np.array([
            # CS_1
            '00000001', '00000000', '________', '00000000',
            '00000001', '00000000', '________', '00000001',
            '00000001', '00000000', '________', '00000010',
            '00000001', '00000000', '________', '00000011',
            # CS_2
            '00000010', '00000000', '________', '00000000',
            '00000010', '00000000', '________', '00000001',
            '00000010', '00000000', '________', '00000010',
            '00000010', '00000000', '________', '00000011',
            # CS_3
            '00000100', '00000000', '________', '00000000',
            '00000100', '00000000', '________', '00000001',
            '00000100', '00000000', '________', '00000010',
            '00000100', '00000000', '________', '00000011'
        ])

        # get 0->255 int that gives voltage out
        voltages = np.rint(128/voltages).astype(int)
        # convert int to 8'b binary representation
        for i in range(len(voltages)):
            SPI[4*i+2] = np.binary_repr(voltages[i], width=8)

        # convert to bytes to send out to FPGA
        byte_pattern = bytearray(int(SPI[i],2) for i in range(len(SPI)))
        self.write(self.address['ldo_spi'], byte_pattern)

    def CISConf(self, data):
        self.wire_in(self.address['chip_rst'], 0x01)
        self.wire_in(self.address['chip_rst'], 0x00)
        arr = np.array(list(data[0]), dtype='|S1')
        for i in range(1,len(data),1):
            arr = np.vstack((arr,np.array(list(data[i]), dtype='|S1')))
        arr=arr.reshape(-1,32)
        # correct inverted captured image
        arrange = np.flip(np.arange(32*3),axis=0) # vertical flip
        data = arr[:3,:].flatten()
        data = data[arrange].reshape(-1,32)
        arr = np.vstack((data,arr[3,:]))

        arrange = np.arange(32).reshape(4,-1)[[3,2,1,0],:]
        arr = arr[:,arrange.flatten()]

        p = arr.flatten().tobytes()
        pattern = bytearray(int(p[i:i+8],2) for i in range(0, len(p), 8))
        print(len(pattern))
        self.write(self.address['cis_spi'], pattern)

    def ImgMapCal(self):
        if os.path.exists(self.img_map_file):
            imgmap = np.load(self.img_map_file)
            print("Loading exist image mapping data...")
        else:
            imgmap = np.arange(self.tab*self.row*self.col*self.ch).reshape(-1,3)
            #imgmap = np.arange(self.tab*self.row*107*self.ch).reshape(-1,3)
            imgmap = np.concatenate(np.vsplit(imgmap,self.row*self.tab),axis=1)
            imgmap = imgmap.transpose().reshape(-1,self.col*self.ch*self.tab)
            #imgmap = imgmap.transpose().reshape(-1,107*self.ch*self.tab)
            np.save(self.img_map_file,imgmap)
            print("Calculating image mapping data...")

        # one column shift in bucket 2
        # h,w=imgmap.shape
        # imgmap[:,int(w/2):w-1] = imgmap[:,int(w/2)+1:w]

        return imgmap

    def int2gray(self,n):
        n ^= (n>>1)
        return bin(n)[2:]

    def ImgGrayMapCal(self):
        if os.path.exists(self.img_graymap_file):
            imgmap = np.load(self.img_graymap_file)
            print("Loading exist image mapping data...")
        else:
            imgmap = np.arange(self.tab*self.row*self.col*self.ch).reshape(-1,3)
            #imgmap = np.arange(self.tab*self.row*107*self.ch).reshape(-1,3)
            imgmap = np.concatenate(np.vsplit(imgmap,self.row*self.tab),axis=1)
            imgmap = imgmap.transpose().reshape(-1,self.col*self.ch*self.tab)
            gray = np.concatenate(([int(self.int2gray(i),2) for i in range(256)],[int(self.int2gray(i),2)+256 for i in range(320-256)]))
            imgmap = imgmap[gray,:]

            np.save(self.img_graymap_file,imgmap)
            print("Calculating image mapping data...")

        # one column shift in bucket 2
        h,w=imgmap.shape
        imgmap[:,int(w/2):w-1] = imgmap[:,int(w/2)+1:w]

        return imgmap

    def MaskMapCal(self,width=512, ch_num=16):
        if False:
        # if os.path.exists(self.mask_map_file):
            imgmap = np.load(self.mask_map_file)
            print("Loading exist mask mapping data...")
        else:
            a = np.arange(width)
            # a = np.concatenate((a[64:],a[:64]))
            a = a.reshape(ch_num,-1)
            # ch_map = np.arange(ch_num)
            ch_map = [8,  9, 10, 11, 12, 13, 14, 15,
                      0,  1,  2,  3,  4,  6,  5,  7]#, 16, 17, 18, 19]
            a[:,:] = a[ch_map,:]
            a = a.flatten()
            ch_width = int(width/ch_num)
            imgmap = a[0::ch_width]
            for i in range(1,ch_width):
                imgmap = np.concatenate((imgmap, a[i::ch_width]))
            np.save(self.mask_map_file,imgmap)
            print("Calculating mask mapping data...")
        return imgmap

    def average_cal(self, time=100, save=False, dir='./image/ave/'):
        """
        calculate the average value of image
        """
        if not os.path.exists(dir):
            os.mkdir(dir)

        black_img = np.zeros([self.row,self.col*self.ch*self.tab])

        for i in range(time):
            black_img += self.arrange_raw(self.imread())
        black_img /= time
        print(black_img)
        np.save(self.black_img_file,black_img)

    def black_cal(self, time=100):
        """
        Cover camera lens and calculate the average value of black level
        """
        black_img = np.zeros([self.row,self.col*self.ch*self.tab])
        for i in range(time):
            black_img += self.arrange_raw(self.imread())
        black_img /= time
        print(black_img)
        np.save(self.black_img_file,black_img)

    def gain_cal(self, num_samps=100):
        cal_mat_gain = np.zeros([self.row,self.col*self.ch*self.tab],dtype=np.float32)
        if (os.path.exists(self.black_img_file)==False):
            # print(~os.path.exists("./image/black_level_calib.npy"))
            print("Perform black level calibration first")
        else :
            for i in range(num_samps):
                cal_mat_gain += self.arrange_raw(self.imread())

            cal_mat_gain = cal_mat_gain/num_samps
            cal_mat_gain = self.black_img - cal_mat_gain
            col_gain_mean = np.mean(cal_mat_gain,0)
            med_gain = np.median(col_gain_mean[10:300])
            coeff_mat = med_gain/col_gain_mean
            print(coeff_mat)
            np.save(self.gain_mat_file,coeff_mat)

    def gain_cal2d(self,num_samps=100):
        bright_img = np.zeros([self.row,self.col*self.ch*self.tab],dtype=np.float32)
        if (os.path.exists(self.black_img_file)==False):
            # print(~os.path.exists("./image/black_level_calib.npy"))
            print("Perform black level calibration first")
        else :
            for i in range(num_samps):
                bright_img += self.arrange_raw(self.imread())

            # #Black calibrated averaged output
            # bright_img  = self.black_img - bright_img/num_samps
            # bright_img[bright_img<0] = 0
            # med_gain    = np.median(bright_img[:240,:])
            # gain_arr    = med_gain/bright_img
            # print(med_gain,gain_arr)
            # np.save(self.gain_cali_file,gain_arr)

        np.save(self.bright_img_file, bright_img/num_samps)

    def param_set(self, addr, value):
        self.wire_in(self.address['param_en'], 0)
        self.wire_in(self.address['param_addr'], addr)
        self.wire_in(self.address['param_in'], value)
        self.wire_in(self.address['param_en'], 1)
        self.wire_in(self.address['param_en'], 0)
        if self.wire_out(self.address['param_out']) != value:
            logging.error("Parameter set failed. A.{}/V.{}".format(addr,value))
            exit(1)

    def readout_reset(self):
        self.wire_in(self.address['chip_rst'], 0x01)
        self.wire_in(self.address['chip_rst'], 0x00)

    def wire_in_check(self, addr, value):
        self.wire_in(addr, value)
        self.wire_check(addr, value)

    def wire_check(self, addr, value):
        self.wire_in(self.address['wire_check_set'], addr)
        cnt = 0
        #while self.wire_out(self.address['wire_check_o']) != value:
        #    self.wire_in(addr, value)
        #    cnt += 1
        #    if(cnt > 10):
        #        logging.error("Wire check failed. A.{}/V.{}".format(addr,value))
        #        exit(1)

    def memory_test(self, msk_size=320*64,msk_num=10):
        # set mask number
        self.wire_in(self.address['pat_num'], msk_num)
        self.wire_in(self.address['mem_pat_num'], msk_num)
        # memory readback mode
        self.wire_in(self.address['mem_mode'], 0x00)
        # reset
        self.wire_in(self.address['mk_routine_rst'], 0x04)
        # write mode
        self.wire_in(self.address['mk_routine_rst'], 0x02)

        times = msk_size*msk_num
        target = bytearray(os.urandom(times))
        result = bytearray(times)

        self.block_write(self.address['mem_wr'], self.BLOCK_SIZE, target)

        # read mode
        self.wire_in(self.address['mk_routine_rst'], 0x04)
        self.wire_in(self.address['mk_routine_rst'], 0x01)
        self.block_read(self.address['mem_rd'], self.BLOCK_SIZE, result)

        for i in range(times):
            if result[i] != target[i]:
                logging.error("Memory Test failed. Total:{} No.{}". \
                              format(times,i))
                for j in range(10):
                    logging.error("Memory Test failed. R:{:02x}/T:{:02x}". \
                                  format(result[i+j],target[j]))
                exit(1)
        logging.info("Memory Test passed.")

    def fmaskupload(self, file_name,dummy=False):
        assert isinstance(file_name, str), "Need a file name"
        assert file_name.lower().endswith('.bmp'), "Wrong file type, should be .bmp"

        pattern = self.loadMask(file_name)
        result = bytearray(len(pattern))

        # reset
        self.wire_in(self.address['mk_routine_rst'], 0x04)
        # write mode
        self.wire_in(self.address['mk_routine_rst'], 0x02)

        if(dummy):
            pass
        else:
            self.block_write(self.address['mem_wr'], self.BLOCK_SIZE, pattern)

        # read mode
        self.wire_in(self.address['mk_routine_rst'], 0x01)

        # check ddr3 calibration finished.
        while True:
            self.dev.UpdateTriggerOuts()
            if self.istriggered(self.address['fifo_flag'], 0x08):
                break;
            else:
                logging.info("DDR3 calibrating...")
                time.sleep(1)

        # memory readback mode
        #self.wire_in(self.address['mem_mode'], 0x00)
        #self.wire_in(self.address['mk_routine_rst'], 0x04)
        #self.wire_in(self.address['mk_routine_rst'], 0x01)
        #length = self.block_read(self.address['mem_rd'], self.BLOCK_SIZE, result)
        #logging.info("Read back length: {}".  format(length))

        #for i in range(len(pattern)):
        #    if result[i] != pattern[i]:
        #        logging.error("Memory Test failed. Total:{} No.{}". \
        #                      format(len(pattern),i))
        #        for j in range(10):
        #            logging.error("Memory Test failed. R:{:02x}/T:{:02x}". \
        #                          format(result[i+j],pattern[i+j]))
        #        exit(1)
        #logging.info("Memory Test passed.")

        # memory mask upload mode
        self.wire_in(self.address['mem_mode'], 0x01)
        self.wire_in(self.address['mk_routine_rst'], 0x04)
        self.wire_in(self.address['mk_routine_rst'], 0x01)

    def loadMask(self, filename, row=320, ch_num=16):
        """
        row: 2D image row number
        """

        file = open(filename, 'rb') #open bmp file
        file.seek(10,0)
        offset = int.from_bytes(file.read(4), 'little') #where the first pixel starts
        file.seek(18, 0)
        width = int.from_bytes(file.read(4), 'little')
        file.seek(22,0)
        height = int.from_bytes(file.read(4), 'little')

        print("2D image row size: {}".format(row))
        print("mask data size: {}x{}".format(height, width))
        print("mask numbers: {:f}".format(height/row))

        # img = cv2.imread(filename,CV2.IMREAD_GRAYSCALE).astype(bool)

        print(height%row)
        assert height%row == 0, "Wrong mask data size"
        imageSize = int(width * height / 8) #in bytes. Using 1 bit per pixel (black or white)

        pattern = bytearray(imageSize)
        mask = bytearray(imageSize)

        file.seek(offset, 0)
        mask = file.read(imageSize)
        file.close()

        toBin = BitArray(bytes=mask).bin
        toArray = np.array(list(toBin),dtype='|S1').reshape(height, width)
        p = toArray[:, self.mkmapping]
        p = p.flatten().tobytes()
        pattern = bytearray(int(p[i:i+8],2) for i in range(0, len(p), 8))

        # resize bytearray to fit blockwrite length
        size = (int(imageSize/512)+1)*512 - imageSize
        logging.info("Added Size: {}".format(imageSize/512.))
        #extra = pattern + pattern + pattern+ pattern+ pattern+ pattern+ pattern+ pattern+ bytearray(size)
        extra = pattern+ bytearray(size)
        print("data arragement finished.")

        return pattern
        #return extra

    def arrange1(self, image):
        col, row, tab, ch = 108, 320, 2, 3

        r_margin = np.full((tab, col*ch), 2**16-1)
        c_margin = np.full((row*tab+tab, 2), 2**16-1)
        temp=np.frombuffer(image, dtype=np.uint16)[:tab*row*col*4].reshape(-1,4)
        img=np.concatenate(np.vsplit(temp[:,:ch],row*tab),axis=1).transpose().reshape(-1,col*ch)
        img=np.concatenate((img, r_margin), axis=0)
        img=np.concatenate((c_margin, img), axis=1)

        i = np.arange(row*tab+tab)
        i = np.concatenate((i[0::tab], i[1::tab]))
        #return img[i,:]
        return self.image_scale(img[i,:])

    def arrange(self, image):
        #temp=np.frombuffer(image, dtype=np.uint16)[:tab*row*col*4].reshape(-1,4)
        #img=np.concatenate(np.vsplit(temp[:,:ch],row*tab),axis=1).transpose().reshape(-1,col*ch*tab)
        #img=np.concatenate(np.vsplit(temp[:,:ch],row*tab),axis=1).transpose().reshape(-1,col*ch)
        # get image data
        #temp=np.frombuffer(image, dtype=np.uint16)[:self.tab*self.row*self.col*4]
        temp=np.frombuffer(image, dtype=np.uint16)
        # delete dummy data
        temp = temp.reshape(-1,4)[:,:self.ch].flatten()
        # mapping data
        img = temp[self.mapping]
        #print(" ".join('{:08b}'.format(x) for x in img[1,4:14]))
        #print(img[1][:10])

        logging.debug(np.mean(img[:,6:312]))
        # logging.debug(" ".join('{:014b}'.format(x) for x in img[1,4:14]))

        # correct left right flip in each individual bucket
        img_yflip = np.zeros(img.shape)
        img_yflip[:,:324] = img[:,:324][:,::-1]
        img_yflip[:,324:] = img[:,324:][:,::-1]

        img_yflip = img

        if(self.gain_flag):
            logging.debug("gain")
            return self.image_scale2(img_yflip)
            #return self.image_scale2(img)
        elif(self.gain_scale_flag):
            logging.debug("gain scale")
            return self.image_scale4(img_yflip)
            #return self.image_scale4(img)
        elif(self.black_flag):
            logging.debug("black")
            return self.image_scale1(img_yflip)
            #return self.image_scale1(img)
        elif(self.black_scale_flag):
            logging.debug("black scale")
            return self.image_scale3(img_yflip)
            #return self.image_scale3(img)
        elif(self.scale_flag):
            logging.debug("normal scale")
            return self.image_scale(img_yflip)
            #return self.image_scale(img)
        else:
            logging.debug("raw")
            return img_yflip
            #return img

    def arrange_raw(self, image, rows):
        taps, colsPerADC, ADCsPerCh, ch = 2, 2, 20, 12
        cols = colsPerADC*ADCsPerCh*ch

        img = np.frombuffer(image,dtype=np.uint8)

        img2=img.reshape(-1,8,4)[:,:,::-1]
        img3=np.unpackbits(img2,axis=2).reshape(-1,256) # recover the bits as stored in fifo

        img4 = img3[:,16:] # get rid of padded bits

        noch    = 12 # total number of channels
        nob     = 12 # number of bits per channel
        img5 = img4.reshape(-1,nob,noch,20)         # convert into 12 separate data channels
        # img6 = np.moveaxis(img5,[1,2,3],[3,1,2])[:,:,::-1]
        img6 = np.moveaxis(img5,1,3)[:,:,:,::-1]
        # img6 = img6[:,:,::-1]
        # at this point
        # AXES:
        #    0: Each conversion (#rows x #taps x #cols/ADC)
        #    1: each digital channel
        #    2: columns serialized by each channel
        #    3: the bits for each column

        img7_shape = np.array(img6.shape); img7_shape[-1] = 16
        img7    = np.zeros(img7_shape,dtype=bool)
        img7[:,:,:,-12:] = img6.copy()
        # img8 = np.packbits(img7,axis=-1).view(np.uint16).reshape(img7_shape[:-1])
        img8    = np.packbits(img7,axis=-1).astype(np.uint16)
        img8    = img8[:,:,:,0]*256+img8[:,:,:,1]
        # col_map = np.arange(20)
        # col_map = [19,9,14,4,18,8,13,3,16,6,11,1,17,7,12,2,15,5,10,0]
        col_map = [0, 4, 12, 8, 16, 2, 6, 14, 10, 18, 1, 5, 13, 9, 17, 3, 7, 15, 11, 19]

        ch_map  = [0,1,2,6,5,3,4,7,8,9,10,11]
        img8    = img8[:,ch_map,:]
        img9 = img8[:,:,[col_map]].reshape(rows,taps,colsPerADC,ADCsPerCh*ch)

        img10 = np.zeros((rows,taps,colsPerADC*ADCsPerCh*ch),dtype=np.uint16)
        img10[:,:,0::2] =   img9[:,:,0,:]
        img10[:,:,1::2] =   img9[:,:,1,:]

        img11 = np.concatenate([img10[:,0,:],img10[:,1,:]],axis=1)

        return img11

    def arrange_raw_T7(self,image,rows):
        tics = [['img0',time.time()]]
        ##rows:480, col:680, taps:2
        taps, colsPerADC, ADCsPerCh, ch = 2, 2, 20, 17
        cols = colsPerADC*ADCsPerCh*ch

        img = np.frombuffer(image,dtype=np.uint8)
        tics.append(['img1',time.time()])

        img2=img.reshape(-1,8,4)[:,:,::-1]
        tics.append(['img2',time.time()])

        img3=np.unpackbits(img2,axis=2).reshape(-1,256) # recover the bits as stored in fifo
        tics.append(['img3',time.time()])

        img4 = img3[:,1:] # get rid of padded bits

        #reorder columns from 15 split unevenly to all 20 in a row
        img4_1 = img4.reshape(-1,17,15)
        img4_1 = np.moveaxis(img4_1,0,1)
        img4_1 = img4_1.reshape(-1,(int(np.shape(img4_1)[1]*15/20)),20)
        img4_1 = np.moveaxis(img4_1,0,1)
        img4_1 = img4_1.reshape(-1,340)
        tics.append(['img4',time.time()])

        noch    = 17 # total number of channels
        nob     = 12 # number of bits per channel
        img5 = img4_1.reshape(-1,nob,noch,20)         # convert into 12 separate data channels
        tics.append(['img5',time.time()])

        # img6 = np.moveaxis(img5,[1,2,3],[3,1,2])[:,:,::-1]
        img6 = np.moveaxis(img5,1,3)[:,:,:,::-1]
        tics.append(['img6',time.time()])

        # at this point
        # AXES:
        #    0: Each conversion (#rows x #taps x #cols/ADC)
        #    1: each digital channel
        #    2: columns serialized by each channel
        #    3: the bits for each column

        # img7_shape       = np.array(img6.shape); img7_shape[-1] = 16
        # img7             = np.zeros(img7_shape,dtype=bool)
        # img7[:,:,:,-12:] = img6
        # pad_loc          = np.zeros((4,2),dtype=int);pad_loc[-1,0]=16-nob
        # img7             = np.pad(img6,[[0,0],[0,0],[0,0],[4,0]])
        img7    = img6
        tics.append(['img7',time.time()])
        img8    = np.packbits(img7,axis=-1).astype(np.uint16)
        tics.append(['img8_packbits',time.time()])
        # img8    = img8[:,:,:,0]*256+img8[:,:,:,1]
        img8      = (img8[:,:,:,0]<<4).astype(np.uint16) + (img8[:,:,:,1]>>4).astype(np.uint16)
        # col_map = np.arange(20)
        # col_map = [19,9,14,4,18,8,13,3,16,6,11,1,17,7,12,2,15,5,10,0]
        col_map = [0, 4, 12, 8, 16, 2, 6, 14, 10, 18, 1, 5, 13, 9, 17, 3, 7, 15, 11, 19]

        ch_map  = np.arange(17)
        ch_map  = [0,1,2,3,4,5,6,7,11,10,8,9,12,13,14,15,16]

        img8    = img8[:,ch_map,:]
        img9 = img8[:,:,[col_map]].reshape(rows,taps,colsPerADC,ADCsPerCh*ch)
        tics.append(['img9',time.time()])

        img10 = np.zeros((rows,taps,colsPerADC*ADCsPerCh*ch),dtype=np.uint16)
        img10[:,:,0::2] =   img9[:,:,0,:]
        img10[:,:,1::2] =   img9[:,:,1,:]
        tics.append(['img10',time.time()])

        img11 = np.concatenate([img10[:,0,:],img10[:,1,:]],axis=1)
        tics.append(['img11',time.time()])

        img12 = img11

        # 2x ODDCOL_EN time
        # img12 = np.zeros(img11.shape, dtype=np.uint16)
        # img12[:,1:680:2] = img11[:,0:680:2]
        # img12[:,0:680:2] = img11[:,680::2]
        # img12[:,681::2] = img11[:,1:680:2]
        # img12[:,680::2] = img11[:,681::2]

        # 2x ROWADD cycles
        img12 = np.zeros(img11.shape, dtype=np.uint16)
        img12[0::2,1:680:2] = img11[:240,0:680:2]
        img12[1::2,1:680:2] = img11[:240,680::2]
        img12[0::2,0:680:2] = img11[240:,0:680:2]
        img12[1::2,0:680:2] = img11[240:,680::2]

        img12[0::2,681::2] = img11[:240,1:680:2]
        img12[1::2,681::2] = img11[:240,681::2]
        img12[0::2,680::2] = img11[240:,1:680:2]
        img12[1::2,680::2] = img11[240:,681::2]

        # for i in range(1,len(tics)):
        #     logging.info("arrange_raw_t7 {}:{}".format(tics[i][0],(tics[i][1]-tics[i-1][1])*1000))

        return img12

    def arrange_adc2(self, image, rows=480, ADCsPerCh=20):
        tics = [['img0',time.time()]]
        ##rows:480, col:680, taps:2
        taps, colsPerADC, ch = 1, 2, 17
        cols = colsPerADC*ADCsPerCh*ch
        # rows = 480
        nuImgs = round(len(image)/taps/cols/rows*8*255/256)
        img = np.frombuffer(image,dtype=np.uint8)
        tics.append(['img1',time.time()])

        img2=img.reshape(-1,8,4)[:,:,::-1]
        tics.append(['img2',time.time()])

        img3=np.unpackbits(img2,axis=2).reshape(-1,256) # recover the bits as stored in fifo
        tics.append(['img3',time.time()])

        img4 = img3[:,1:] # get rid of padded bits

        #reorder columns from 15 split unevenly to all 20 in a row
        img4_1 = img4.reshape(-1,17,15)
        img4_1 = np.moveaxis(img4_1,0,1)
        img4_1 = img4_1.reshape(-1,(int(np.shape(img4_1)[1]*15/ADCsPerCh)),ADCsPerCh)
        img4_1 = np.moveaxis(img4_1,0,1)
        img4_1 = img4_1.reshape(-1,17*ADCsPerCh)
        tics.append(['img4',time.time()])

        noch    = 17 # total number of channels
        nob     = 1  # number of bits per channel
        img5 = img4_1.reshape(-1,nob,noch,ADCsPerCh)         # convert into 12 separate data channels
        tics.append(['img5',time.time()])

        # img6 = np.moveaxis(img5,[1,2,3],[3,1,2])[:,:,::-1]
        img6 = np.moveaxis(img5,1,3)[:,:,:,::-1]
        tics.append(['img6',time.time()])

        # img3 = np.unpackbits(img2,axis=2).reshape(-1,256*4).astype(bool)
        # tics.append(['img3',time.time()])
        # img6 = img3[:,self.adc2_map_1row]
        # tics.append(['img6',time.time()])
        # img6=img6.reshape(-1,ch,ADCsPerCh,nob)
        # tics.append(['img6_reshape',time.time()])

        # at this point
        # AXES:
        #    0: Each conversion (#rows x #taps x #cols/ADC)
        #    1: each digital channel
        #    2: columns serialized by each channel
        #    3: the bits for each column

        img7_shape = np.array(img6.shape); img7_shape[-1] = 1; img7_shape[2]=ADCsPerCh
        img7    = np.zeros(img7_shape,dtype=bool)
        img7[:,:,:ADCsPerCh,-nob:] = img6.copy()
        tics.append(['img7',time.time()])

        # img8 = np.packbits(img7,axis=-1).view(np.uint16).reshape(img7_shape[:-1])
        img8    = img7.copy()
        tics.append(['img8',time.time()])

        # col_order = np.arange(ADCsPerCh)
        col_order =  [19, 9, 14, 4, 18, 8,13, 3,  16, 6, 11, 1, 17, 7, 12, 2,15, 5, 10, 0]
        # col_map =  [0, 4, 12, 8, 16, 2, 6, 14, 10, 18, 1, 5, 13, 9, 17, 3, 7, 15, 11, 19]
        col_map = np.argsort(col_order[:ADCsPerCh])[::-1]

        ch_map  = np.arange(ch)
        ch_map  = [0,1,2,3,4,5,6,7,11,10,8,9,12,13,14,15,16]

        img8    = img8[:,:,col_map]
        tics.append(['img8_mapped',time.time()])

        img9 = img8[:,ch_map,:].reshape(nuImgs,rows,taps,colsPerADC,ADCsPerCh*ch)
        tics.append(['img9',time.time()])

        img10 = np.zeros((nuImgs,rows,taps,colsPerADC*ADCsPerCh*ch),dtype=np.uint16)
        img10[:,:,:,0::2] =   img9[:,:,:,0,:]
        img10[:,:,:,1::2] =   img9[:,:,:,1,:]
        tics.append(['img10',time.time()])

        # for i in range(1,len(tics)):
        #     logging.info("arrange_adc2 {}:{}".format(tics[i][0],(tics[i][1]-tics[i-1][1])*1000))

        return img10.reshape(nuImgs,rows,cols)

    def image_scale6(self,image,gain=False,black=True,dynamic=False,max_scale = 3000):

        [row,col,tab] = [480, 680, self.tab]

        x1 = self.black_img.astype(np.float32)
        y1 = np.median(x1)

        y2 = np.zeros((row,col*tab)) # target median

        if(gain):
            # assert black==True, "Black cal must be ON if gain needs to be ON"
            x2 = self.bright_img.astype(np.float32)
            y2_left  = np.median(x2[:,30:col-10].flatten()) # target median left tap
            y2_right = np.median(x2[:,col+30:2*col-10].flatten()) # target median right tap

            y2[:,:col] = y2_left
            y2[:,col:] = y2_right

            # y2 = np.median(x2)
            [num,den] = [(y2-y1),(x2-x1)]
            num[den==0] = 0
            den[den==0] = np.inf
            m = num/den
            c = y1-num/den*x1

            logging.debug(np.mean(m[100:110,100:110]))
            img = m*image + c
            img = y1 - img
            img[img<0] = 0

        elif(black):
            img = self.black_img - image
            img[img<0] = 0
        elif(dynamic):
            percentile = 5
            min_lvl = np.percentile(np.concatenate((image[:,:320],image[:,324:644]),axis=1),5)
            max_lvl = np.percentile(image,95)
            img = 2**16-1 - (np.clip(image, min_lvl, max_lvl)-min_lvl)*(2**16-1)/(max_lvl-min_lvl)
            return img.astype(np.uint16)
        else:
            img = image.astype(np.float32)
        # Clip values between 0 and 3000 and scale to 0 to 2**16-1
        img = (np.clip(img,0,max_scale)*(2**16-1)/max_scale).astype(np.uint16)

        return img

    def imread(self):
        # container = bytearray(self.frame_length)
        cnt = 0
        cnt1=1
        #self.trigger_in(self.address['trigger'], 0)
        while True:

            #time.sleep(0.05)
            self.dev.UpdateTriggerOuts()
            if self.istriggered(self.address['fifo_flag'], 0x02):
                logging.debug("fifo_cnt: "+str(self.wire_out(self.address['fifo_cnt'])))
                self.dev.UpdateTriggerOuts()
                self.read(self.address['image'], self.adc1_container)
                #print("".join('{:02x}'.format(x) for x in container[:8]))
                #print(" ".join('{:08b}'.format(x) for x in container[:8]))
                #toBin = BitArray(bytes=container).bin
                #print(toBin[:16]+" "+toBin[16:32]+" "+toBin[32:48]+" "+toBin[48:64])
                #self.trigger_in(self.address['trigger'], 0)
                return self.adc1_container

            cnt += 1
            #self.state_check();
            if cnt % 1000 == 999:
                bt=self.wire_out(self.address['fifo_cnt'])
                print("fifo_cnt: "+str(bt))
                print(cnt1)
                cnt1 += 1
                print("empty: "+str(self.istriggered(self.address['fifo_flag'], 0x04)))
                print("prg_full: "+str(self.istriggered(self.address['fifo_flag'], 0x02)))
                print("full: "+str(self.istriggered(self.address['fifo_flag'], 0x01)))
                #self.read(self.address['image'], container)

    def adc2_read(self,NSUB):
        cnt = 0
        self.dev.UpdateTriggerOuts()
        while True:
            if self.istriggered(self.address['fifo_flag'], 0x10):
                self.read(self.address['adc2_rd'],self.adc2_container)
                self.dev.UpdateTriggerOuts()

            cnt+=1
            if(cnt%1000==999):
                logging.info('stuck reading adc2 cnt:{:04d}'.format(cnt))
            # t6.read(0xa3,pread)
            return self.adc2_container

    def imsave(self, image, dir, filename,full=False):
        if not os.path.exists(dir):
            os.mkdir(dir)
        #pattern =re.compile(".png")
        #if pattern.match(filename):

        if filename.find(".png")!=-1:
            #print(image.dtype)
            if full:
                numpngw.write_png(dir+filename,image)
            else:
                #scipy.misc.imsave(dir+filename,image)
                cv2.imwrite(dir+filename, image)
        #pattern =re.compile(".npy")
        #if pattern.match(filename):
        if filename.find(".npy")!=-1:
            #print(image.dtype)
            np.save(dir+filename,image)
        if filename.find(".npz")!=-1:
            #print(image.dtype)
            np.savez_compressed(dir+filename,img=image)

if __name__ == "__main__":

    #bitfile = "./bitfile/t4/Reveal_Top_multi3.bit"
    bitfile="bitfile/Reveal_Top_3.bit"
    # bitfile="bitfile/Reveal_Top.bit"

    t6=T6(bitfile)
    # t6.param_set(self.param['Select'], 0x80000000)  # disable slave mode
    # t6.fmaskupload("./maskfile/t4_merge.bmp")
    # t6.set_exposure(10000)
    # t6.set_pattern_num(10)

    input("Press enter after SPI")

    t6.UploadMaskDummy('',10)
    t6.SetMaskParam(5,2)
    t6.SetExposure(2000)
    t6.readout_reset()
    t6.gain_flag = False
    t6.black_flag = False
    t6.black_scale_flag = False
    t6.imshow()


import math

class Tab:
    # syntax: https://www.guitartricks.com/helptab.php

    # TODO: import/export
    # TODO: write to latex
    # TODO: insert_time (beat, snap, num_snaps) -> adds num_snaps time at (beat, snap)
    # TODO: delete(beat, snap, num_snaps) -> removes num_snaps snaps beginning in (beat, snap)
    # TODO: play tab: https://realpython.com/playing-and-recording-sound-python/
    # TODO: allow to insert multiple fret at the same time (in different chords, for the same beat)

    def __init__(
        self,
        strings = ["e", "B", "G", "D", "A", "E"],
        beats_per_bar = 4,
        snaps_per_beat = 4
        ):
        self.__max_len = 150

        # here string denotes a guitar string
        self.__strings = strings
        self.__max_string_len = max([len(i) for i in self.__strings])
        self.__string_sep = "|"

        # a bar is a set of beats in a specific tempo
        self.__bar_sep = "|"

        # a beat is the basic rhythmic unit of measure
        self.__beats_per_bar = beats_per_bar
        self.__beat_marker = "*"

        # a snap is a subdivision of beats
        self.__snaps_per_beat = snaps_per_beat
        self.__snap_char = "-"
        self.__snap_len = 2
        self.__snap_marker = "."

        self.__set_bars_per_line()

        # { beat: { snap: { string: fret } } }
        self.__music = {}

    # calculate ideal bars_per_line based on max line length, beat_snap, etc
    # if snap_len is increased by some fret, it adjusts the lines per bar
    def __set_bars_per_line(self):
        bar_len = self.__snap_len * self.__snaps_per_beat * self.__beats_per_bar
        self.__bars_per_line = (self.__max_len - self.__max_string_len - len(self.__string_sep)) // bar_len
        if self.__bars_per_line == 0:
            self.__bars_per_line = 2

    # basic add operation: adds a fret in string, in a snap of a beat
    def add(self, string, fret, beat=False, snap=False):
        if string not in self.__strings:
            raise ValueError(f"Tab.add - Invalid argument: string must be one of {self.__strings}")

        if type(fret) is not str:
            raise ValueError(f"Tab.add - Invalid argument: fret must be a string")

        if (beat == False and snap != False) or (beat != False and snap == False):
            raise ValueError("Tab.add - Invalid argument: cannot specify a snap without specifying a beat or vice versa")

        if beat != False and (type(beat) is not int or beat < 1):
            raise ValueError("Tab.add - Invalid argument: beat must be a positive integer")

        if snap != False and (type(snap) is not int or not (1 <= snap <= self.__snaps_per_beat)):
            raise ValueError(f"Tab.add - Invalid argument: snap must be an integer in range [1, {self.__snaps_per_beat}]")

        # Need to find next beat/snap
        if beat == False:
            beats = self.__music.keys()
            if len(beats) == 0:
                beat = 1
                snap = 1
            else:
                max_beat = max(self.__music.keys())
                max_snap = max(self.__music[max_beat].keys())

                if max_snap == self.__snaps_per_beat:
                    beat = max_beat + 1
                    snap = 1
                else:
                    beat = max_beat
                    snap = max_snap + 1

        if beat not in self.__music:
            self.__music[beat] = {}

        if snap not in self.__music[beat]:
            self.__music[beat][snap] = {}

        self.__music[beat][snap][string] = fret

        # adjust output snap length if necessary
        if len(fret) >= self.__snap_len:
            self.__snap_len = len(fret) + 1
            # do not forget to update bars per line
            self.__set_bars_per_line()

    # This funcion returns the item that is stored for that beat, snap and string
    def get(self, beat, snap, string):
        # TODO: check argument validity
        if beat not in self.__music or snap not in self.__music[beat] or string not in self.__music[beat][snap]:
            return self.__snap_char * self.__snap_len

        item = self.__music[beat][snap][string]
        return f'{item}{self.__snap_char * (self.__snap_len - len(item))}'

    def __repr__(self):
        res = ""

        n_bars = math.ceil(max(self.__music.keys()) / self.__beats_per_bar)
        n_lines = math.ceil(n_bars / self.__bars_per_line)

        line_beat = 1

        for line in range(n_lines):

            # represent snap/beat division in each line
            snap_beat_marker = " " * (self.__max_string_len + 1) # account for space
            snap_beat_marker += self.__string_sep
            for _ in range(self.__bars_per_line):
                for _ in range(self.__beats_per_bar):
                    snap_beat_marker += self.__beat_marker + " " * (self.__snap_len - 1)
                    for _ in range(self.__snaps_per_beat - 1):
                        snap_beat_marker += self.__snap_marker + " " * (self.__snap_len - 1)
                snap_beat_marker += self.__bar_sep

            res += snap_beat_marker + "\n"

            # represent frets for each string
            for string in self.__strings:
                cur_beat = line_beat
                res += f'{" " * (self.__max_string_len - len(string))}{string} {self.__string_sep}'
                for bar in range(self.__bars_per_line):
                    for beat in range(self.__beats_per_bar):
                        for snap in range(self.__snaps_per_beat):
                            res += self.get(cur_beat, snap + 1, string)

                        cur_beat += 1
                    res += self.__bar_sep
                res += '\n'

            line_beat = cur_beat

            res += '\n'

        return res


import datetime
from os import path

from .filenameParser import FilenameParser
from .fops import getFileList, moveFile
from .subtitle import Subtitle

class Movie():

    file                = None
    estimatedTitle      = None
    estimatedTitleFrags = None
    databaseTitle       = None
    databaseId          = None
    databaseYear        = None
    estimatedYear       = None
    subs                = None

    def __init__(self):
        self.file                = FilenameParser()
        self.estimatedTitle      = ''
        self.estimatedTitleFrags = []
        self.databaseTitle       = ''
        self.databaseYear        = 1900
        self.databaseId          = -1
        self.estimatedYear       = 1900
        self.subs                = []

    def __isYear__(self, string : str):
        if not string.isdecimal():
            return [False, 0]

        y = int(string)
        if y > 1900 and y <= datetime.datetime.now().year:
            return [True, y]

        return [False, 0]

    def parse(self, name : str, rootFolder):
        self.file.parse(name, rootFolder)

        for s in self.file.parsedNames:
            result = self.__isYear__(s)

            if result[0]:
                self.estimatedYear = result[1]
                break

        for s in self.file.parsedNames:
            if s.lower() in self.file.cutter:
                break
            self.estimatedTitleFrags.append(s)

        self.estimatedTitle      = ' '.join(self.estimatedTitleFrags)

        # Check for subtitles
        subsPath = self.file.filepath + '/subs'
        if path.exists(subsPath) and path.isdir(subsPath):
            self.getSubtitles(subsPath)

        subsPath = self.file.filepath + '/subtitles'
        if path.exists(subsPath) and path.isdir(subsPath):
            self.getSubtitles(subsPath)

    def getSubtitles(self, path):
        files = getFileList(path, [])
        for file in files:
            sub = Subtitle()
            if sub.parse(file, path, self.file.fileName):
                self.subs.append(sub)

    def setTarget(self, target : str):
        target = target.replace("\\", "/")
        self.file.targetFileName = target

        if len(self.subs) == 0:
            return
        pos        = target.rfind("/") + 1
        subsFolder = target[:pos] + 'Subs/'
        filename   = target[pos:target.rfind('.')]

        for sub in self.subs:
            sub.file.targetFileName = subsFolder + filename + '-' + sub.lang + '.' + sub.file.extension

    def move(self, overwrite):
        if self.file.targetFileName == '':
            return

        moveFile(self.file.fullNameAndPath, self.file.targetFileName, overwrite)
        for sub in self.subs:
            moveFile(sub.file.fullNameAndPath, sub.file.targetFileName, overwrite)

    def serialize(self):
        j = dict()
        j['file']                = self.file.serialize()
        j['estimatedTitle']      = self.estimatedTitle
        j['estimatedTitleFrags'] = self.estimatedTitleFrags
        j['databaseTitle']       = self.databaseTitle
        j['databaseYear']        = self.databaseYear
        j['databaseId']          = self.databaseId
        j['estimatedYear']       = self.estimatedYear

        subs = []
        for sub in self.subs:
            subs.append(sub.serialize())
        j['subs']                = subs

        return j

    def deserialize(self, j: dict()):
        self.__init__()
        self.file                = FilenameParser()
        self.file.deserialize(j['file'])
        self.estimatedTitle      = j['estimatedTitle']
        self.estimatedTitleFrags = j['estimatedTitleFrags']
        self.databaseTitle       = j['databaseTitle']
        self.estimatedYear       = j['estimatedYear']
        self.databaseYear        = j['databaseYear']
        self.databaseId          = j['databaseId']
        for sub in j['subs']:
            self.subs.append(Subtitle().deserialize(sub))


import pandas as pd
import time
from utils.query_processing import get_wordnet_pos, process_query, expand_query
from basic_bm25 import bm25_basic, get_result
from utils.query_processing import get_wordnet_pos, process_query, expand_query
from bm25_with_pseudo_relevance import bm25_pseudo_relevance_back

tf_relevance_feedback = pd.read_csv('../data/trained_scores/course_info_with_survey_tf_trained.csv', header=0, index_col=0)
tf_norm_relevance_feedback = pd.read_csv('../data/trained_scores/course_info_with_survey_tf_norm_trained.csv', header=0, index_col=0)
idf_relevance_feedback = pd.read_csv('../data/trained_scores/course_info_with_survey_idf_trained.csv', header=0, index_col=0)
vocab = tf_relevance_feedback.index.tolist()  # unique words
total_length = tf_relevance_feedback.to_numpy().sum()
avg_doc_len = total_length / len(tf_relevance_feedback.columns) # average document length across all courses
glove_kv = '../pretrained_corpus/glove_6B_300d.kv'

query = "network, term, model, technology, probability"

# basic bm25 without query expansion
start_time = time.time()
query_1 = process_query(query=query)
result, ls = get_result(query=query_1,tf=tf_relevance_feedback,tf_norm=tf_norm_relevance_feedback,idf=idf_relevance_feedback,vocab=vocab,avg_doc_len=avg_doc_len)
print(f"time taken for basic bm25 without query expansion: {time.time() - start_time}")

# basic bm25 with query expansion
start_time = time.time()
query_2 = process_query(query=query)
query_2 = expand_query(query,glove_kv,topn=3)
result, ls = get_result(query=query,tf=tf_relevance_feedback,tf_norm=tf_norm_relevance_feedback,idf=idf_relevance_feedback,vocab=vocab,avg_doc_len=avg_doc_len)
print(f"time taken for basic bm25 with query expansion: {time.time() - start_time}")

# bm25 with pseudo relevance feedback
start_time = time.time()
df_relevance_feedback = pd.read_csv('../data/trained_scores/course_info_with_survey_df_trained.csv', header=0, index_col=0)
norm_association_matrix = pd.read_csv('../data/trained_scores/norm_association_matrix_trained.csv', header = 0, index_col = 0)

map_pseudo_relevance_feedback = result, ls = bm25_pseudo_relevance_back(query=query, df=df_relevance_feedback, tf=tf_relevance_feedback, tf_norm=tf_norm_relevance_feedback, idf=idf_relevance_feedback, norm_association_matrix=norm_association_matrix, vocab=vocab, avg_doc_len=avg_doc_len, k=10)
print(f"time taken for basic bm25 with pseudo relevance feedback: {time.time() - start_time}")


from project.tennis_player import TennisPlayer
import unittest

class TennisPlayerTest(unittest.TestCase):
    def setUp(self) -> None:
        self.player = TennisPlayer("Gosho", 18, 66)

    def test_init(self):
        self.assertEqual(self.player.name, "Gosho")
        self.assertEqual(self.player.age, 18)
        self.assertEqual(self.player.points, 66)
        self.assertEqual(self.player.wins, [])

    def test_validations_init(self):
        with self.assertRaises(ValueError) as context:
            self.player.name = ""

        self.assertEqual(str(context.exception), "Name should be more than 2 symbols!")

        with self.assertRaises(ValueError) as context:
            self.player.name = "GG"

        self.assertEqual(str(context.exception), "Name should be more than 2 symbols!")

        with self.assertRaises(ValueError) as context:
            self.player.age = 5

        self.assertEqual(str(context.exception), "Players must be at least 18 years of age!")

    def test_win_method(self):
        self.player.add_new_win("Wimbeldon")
        self.assertEqual(self.player.wins, ["Wimbeldon"])
        self.assertEqual(self.player.add_new_win("Wimbeldon"), "Wimbeldon has been already added to the list of wins!")

    def test_lt_method(self):
        player2 = TennisPlayer("Pesho", 18, 67)
        self.assertEqual('Pesho is a top seeded player and he/she is better than Gosho', self.player.__lt__(player2))
        player2.points = 33
        self.assertEqual('Gosho is a better player than Pesho', self.player.__lt__(player2))

    def test_str_method(self):
        self.assertEqual("Tennis Player: Gosho\n"
                         "Age: 18\n"
                         "Points: 66.0\n"
                         "Tournaments won: ", self.player.__str__())

        self.player.add_new_win("Shelf")
        self.player.add_new_win("Iron")

        self.assertEqual("Tennis Player: Gosho\n"
                         "Age: 18\n"
                         "Points: 66.0\n"
                         "Tournaments won: Shelf, Iron", self.player.__str__())

if __name__ == '__main__':
    unittest.main()


import uvicorn
from fastapi import FastAPI, Form
from standarting_phone import unify_phone
from fastapi.responses import Response

app = FastAPI()

@app.post("/unify_phone_from_form")
def return_the_phone(phone: str = Form(...)):
    # content_type = .headers.get('Content-Type')

    # if content_type is None:
        # return 'No content-type provided'
    # elif content_type == "multipart/form-data":
        try:
            unified_phone = unify_phone(phone)
            response = Response(
                f"{unified_phone}",
                media_type = "text/html"
            )
            return response
        except ValueError:
            return "Invalid data"
    # else:
        # return "Content-Type not supported"

if __name__ == "__main__":
    uvicorn.run(app, host="195.135.253.40", port=8000)


'''
This script will compare the files in the two provided directories (if the old
catalog exists) and produce a delta file that enumerates which files have changed.
This file will be saved in the appropriate directory within server_path (a variable
set at the top of this script). The version number is automatically detected based
on which delta files are already present in this location.
'''

import os
import sys
import shutil

semester_prefix = "sem-"
delta_prefix = "delta-"
delta_separator = "#,#"
requirements_dir_name = "requirements"
excluded_file_names = ["features.txt"]

# These file names will be concatenated to the delta file if the version number is 1.
first_version_file_names = ["departments", "enrollment"]

def write_delta_file(semester_name, delta, outpath):
    # Determine version number
    version_num = 1
    if os.path.exists(outpath):
        while os.path.exists(os.path.join(outpath, delta_prefix + str(version_num) + ".txt")):
            version_num += 1
    else:
        os.mkdir(outpath)
    if version_num == 1:
        delta = delta + first_version_file_names

    comps = semester_name.split('-')
    delta_file_path = os.path.join(outpath, delta_prefix + str(version_num) + ".txt")
    with open(delta_file_path, 'w') as file:
        if semester_name != requirements_dir_name:
            file.write(delta_separator.join(comps) + "\n")
        else:
            file.write("\n")
        file.write(str(version_num) + "\n")
        file.write("\n".join(delta))

    print("Delta file written to {}.".format(delta_file_path))

def delta_file_name(path):
    if ".txt" in path:
        return path[:path.find(".txt")]
    elif ".reql" in path:
        return path[:path.find(".reql")]
    return path

def make_delta(new_directory, old_directory):
    """Computes a list of file names that have changed between the old directory
    and the new directory."""
    delta = []
    for path in os.listdir(new_directory):
        if path[0] == '.' or path in excluded_file_names: continue
        old_path = os.path.join(old_directory, path)
        if not os.path.exists(old_path):
            delta.append(delta_file_name(path))
            continue
        with open(os.path.join(new_directory, path), 'r') as new_file:
            with open(old_path, 'r') as old_file:
                new_lines = new_file.readlines()
                old_lines = old_file.readlines()
                if new_lines != old_lines:
                    delta.append(delta_file_name(path))
    return delta

def commit_delta(new_directory, old_directory, server_path, delta):
    """Writes the delta to file, and moves the contents of new_directory into
    old_directory (preserving the old contents in an '-old' directory)."""

    old_name = os.path.basename(old_directory)
    if semester_prefix in old_name:
        semester_name = old_name[old_name.find(semester_prefix) + len(semester_prefix):]
    else:
        semester_name = old_name

    write_delta_file(semester_name, delta, os.path.join(server_path, old_name))
    old_dest = os.path.join(os.path.dirname(old_directory), old_name + "-old")
    if os.path.exists(old_dest):
        shutil.rmtree(old_dest)
    if os.path.exists(old_directory):
        shutil.move(old_directory, old_dest)
    shutil.move(new_directory, old_directory)

if __name__ == '__main__':
    if len(sys.argv) < 4:
        print("Insufficient arguments. Pass the directory of new files, the directory that the files should be saved to (e.g. sem-spring-2018), and the path to the courseupdater directory in the server. Make sure you do not add trailing slashes to your paths.")
        exit(1)
    new_directory = sys.argv[1]
    old_directory = sys.argv[2]
    server_path = sys.argv[3]
    print("Changed items:")
    delta = make_delta(new_directory, old_directory)
    for file in delta:
        print(file)
    if raw_input("Ready to write files?") in ['y', 'yes', '\n']:
        commit_delta(new_directory, old_directory, server_path, delta)
        print("Old files moved to {}. New files moved to {}.".format(os.path.join(os.path.dirname(old_directory), old_name + "-old"), old_directory))
    else:
        print("Aborting.")


import sys
import os
import subprocess
import argparse

description = 'A quick and dirty way to create a living youtube playlist downloader'

def main():
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument('-p','--path of Playlist.txt',
        help='Path of Playlist.txt file and folder', required=True)
    args = vars(parser.parse_args())
    plist_path = os.path.abspath(args['path of Playlist.txt'])
    playlist = open(plist_path,'r')
    for line in playlist.readlines():
          subprocess.call(['youtube-dl', line])

if __name__ == '__main__':
    main()