import subprocess
import os
import csv
import sys
from sys import platform as _platform
import traceback
import argparse
import re

# Platform
os_platform = ""

if _platform == "linux" or _platform == "linux2":
    os_platform = "linux"
elif _platform == "darwin":
    os_platform = "macos"
elif _platform == "win32":
    os_platform = "windows"

# Win32 Imports
if os_platform == "windows":
    try:
        import wmi
        import win32api
        from win32com.shell import shell
        import win32file
    except Exception as e:
        print("Linux System - deactivating process memory check ...")
        os_platform = "linux"  # crazy guess

if os_platform == "":
    print("Unable to determine platform - LOKI is lost.")
    sys.exit(1)

class Node:
    def __init__(self, pid, ppid, image_file_name):
        self.pid = pid
        self.ppid = ppid
        self.image_file_name = image_file_name
        self.children = []

class AutoVol3(object):

    normal_proc = {}
    suspect_proc = {}

    regex_patterns = []
    whitelist_paths = []
    normal_paths =[]
    normal_paths_x86 =[]        # The normal path for a process if x86
    normal_process_path = []    # The normal path for a process
    process_path = {}           # The path for the running process from dlllist
    normal_sids = {}

    suspecious_proc_sids = {}
    suspect_cmdlines = {}
    suspect_netscan = {}

    dll_stacking = {}

    process_tree = {}
    pslist = {}
    psscan ={}
    dlllist = {}
    cmdline = {}
    netscan ={}
    getsids ={}
    handles = {}

    baseline_proc = {}
    evidence_bag = []

    score = {}

    plugins = ['pslist', 'psscan', 'pstree', 'dlllist', 'cmdline', 'netstat', 'netscan', 'handle', 'getsids']

    analyze = ['List suspecious processes from EPROCESS', 'List suspecious processes with memory scanning', 'List suspecious cmdlines', 'List suspecious DLLs',
               'List suspecious communications from EPROCESS', 'List suspecious communications with memory scanning', 'Compare the suspect image with baseline',
               'List processes running with suspecious account']

    csv_files = ['pslist.csv', 'psscan.csv', 'pstree.csv', 'dlllist.csv', 'cmdline.csv', 'netstat.csv', 'netscan.csv', 'handles.csv', 'getsids.csv']

    def __init__(self, image):

        self.image = image
        self.app_path = get_application_path()

        self.csvgen(os.path.join(self.app_path, args.p))
        self.initialize_normal_proc(os.path.join(self.app_path, 'normal_proc.txt'))
        self.initialize_normal_paths(os.path.join(self.app_path, 'normal_paths.txt'))
        self.initialize_normal_sids(os.path.join(self.app_path, 'normal_sids.txt'))
        self.initialize_whitelist_paths('whitelist.txt')
        self.initialize_regex_patterns('regex_patterns.txt')

        # Read the required file from running the volatility plugins.
        self.csv_reader('pslist.csv')
        self.csv_reader('psscan.csv')
        self.csv_reader('dlllist.csv')
        self.csv_reader('cmdline.csv')
        self.csv_reader('netscan.csv')
        self.csv_reader('getsids.csv')
        self.csv_reader('handles.csv')
        self.malhandles()

        # Analyzing memory for suspicious processes
        self.procTree(os.path.join(self.app_path, args.p) + '/'+'pslist.csv')
        self.malproc(self.process_tree['4'])
        self.malpath()
        self.malcmdline()
        self.malcomm('blacklist.txt')
        self.baseline('proc_baseline.txt')
        self.malgetsids()
        self.find_processes_without_parents()

        # The following function must be run the last one
        self.malicious_weight()

        header = ['Source Name', 'PID', 'PPID','Process Name', 'Path', 'Timestamps', 'Long Description']
        self.evidence_bag.append(','.join(header))

    def run_plugin(self, memory_image_path, output_file):

        # if output_file == 'netscan.csv':
        #     command = ["vol2.py", "--profile", "Win10x64_15063", "-f", memory_image_path, output_file[:-4]]

        # else:
        command = ["vol", "-r", "csv", "-f", memory_image_path, 'windows.'+output_file[:-4]]

        try:
            result = subprocess.run(command, capture_output=True, text=True, check=True)
            output = result.stdout
            # Save the result to the specified CSV file
            with open(os.path.join(self.app_path, args.p)+'/'+output_file, 'w', encoding='utf-8') as csv_file:
                csv_file.write(output)

        except subprocess.CalledProcessError as e:
            print("Error:", e)
            print("Return Code:", e.returncode)
            print("Command Output:", e.output)
        except Exception as e:
            print("An error occurred:", str(e))

    def csvgen(self, memory_image_path):

        # Specify the directory path you want to list files from
        #directory_path = "./memory"

        try:

            # Use the os.listdir() function to get a list of files in the directory
            files = os.listdir(memory_image_path)

            # Check if there are any .img files in the directory
            img_files = [file for file in files if file.endswith(".img")]

            if not img_files:
                print(f"No '.img' files found in '{memory_image_path}'.")
            else:
                # Loop through and print the names of .img fil

                for file in self.csv_files:

                    if file not in files:
                        print('File not found:', file)
                        print('Generate file:', file,'...')
                        self.run_plugin(memory_image_path+'/'+img_files[0],file)

                        # if file == 'dlllist.csv':
                        #     result = list_dlls(img_files[0])
                        # elif file == 'cmdline.csv':

        except FileNotFoundError:
            print(f"The directory '{memory_image_path}' does not exist.")
        except PermissionError:
            print(f"You do not have permission to access '{memory_image_path}'.")
        except Exception as e:
            print(f"An error occurred: {str(e)}")

    def baseline(self, baseline_file):
        try:
            # Use the os.listdir() function to get a list of files in the directory
            fpath = os.path.join(self.app_path, args.p)
            files = os.listdir(fpath)

            # If proc_baseline.txt is not found, uncomment the next two lines to create it. It will takkke sometime. Be patient
            if baseline_file not in files:
                # Define the command as a list of strings
                print('proc_baseline.txt file was found, generating this file ...')
                command = ["python3", "/opt/memory-baseliner/baseline.py", "-proc", "-i", "/cases/memory/base-rd01-memory.img",
                "--loadbaseline", '--jsonbaseline', '/cases/precooked/memory/Win10x64_proc.json', '-o', fpath+'/'+'proc_baseline.txt']
                # Run the command and capture its output
                completed_process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

        except FileNotFoundError:
            print(f"The directory '{fpath}' does not exist.")
        except PermissionError:
            print(f"You do not have permission to access '{fpath}'.")
        except Exception as e:
            print(f"An error occurred: {str(e)}")

        # Read the content of the input file and replace '|' with ','
        with open(fpath+'/'+'proc_baseline.txt', "r") as input_file:
            content = input_file.read()
            modified_content = content.replace("|", ",")

            modified_content = modified_content.strip().split('\n')[1:]

            for row in modified_content:
                columns = row.split(',')
                pid = columns[0]

                if '.exe' in columns[6]:
                    # if pid not in self.baseline_proc:
                    #     self.baseline_proc[pid] = []
                    self.baseline_proc[pid.strip('"')] = [''.join(row)]
        #print(self.baseline_proc)
        # Write the modified content back to the output file
        # with open(fpath+'/'+'suspecious_proc.txt', "w") as output_file:
        #     output_file.write(modified_content)

        # print("Replacement complete. Output written to", 'suspecious_proc.txt')

    def initialize_normal_proc(self, normal_proc_file):

        # Open and read the "normal_proc.txt" file
        with open(normal_proc_file, 'r') as file:
            for line in file:
                # Split each line into parent and child using ':' as the separator
                parent, child = line.strip().split(':')

                # If the parent is not in the dictionary, add it with an empty list of children
                if parent not in self.normal_proc:
                    self.normal_proc[parent] = []

                # Append the child to the parent's list of children
                self.normal_proc[parent].append(child)

        # The resulting dictionary is parsed_normal_proc

    def initialize_whitelist_paths(self, whitelist_file):

        # Open and read the "normal_proc.txt" file
        # You need to change this list "normal_proc.txt", because it is not clean, missing a lot of system files and dlls.
        with open(whitelist_file, 'r') as file:
            for line in file:
                # Split each line into parent and child using ':' as the separator
               self.whitelist_paths.append(line.strip('\n').split(',')[0].lower())
               #print(self.whitelist_paths)

    def initialize_normal_sids(self, normal_sids_file):

        # Open and read the "normal_proc.txt" file
        with open(normal_sids_file, 'r') as file:
            for line in file:
                # Split each line into parent and child using ':' as the separator
                parent, child = line.strip().split(':')

                # If the parent is not in the dictionary, add it with an empty list of children
                if parent not in self.normal_sids:
                    self.normal_sids[parent] = []

                # Append the child to the parent's list of children
                self.normal_sids[parent].append(child)

    def initialize_normal_paths(self, fpath):
        try:

            with open(fpath, newline='') as file:

                for line in file:
                   # self.normal_process_path.append(line.strip('\n').split('\\')[-1])

                    if 'program files (x86)' in line:
                        self.normal_paths_x86.append(line.strip('\n'))
                    else:
                        self.normal_paths.append(line.strip('\n'))

        except FileNotFoundError:
            print(f"The directory '{fpath}' does not exist.")
        except PermissionError:
            print(f"You do not have permission to access '{fpath}'.")
        except Exception as e:
            print(f"An error occurred: {str(e)}")

    def initialize_blacklist_addresses(self, file_path):
    # Read the blacklist addresses from a file and return them as a set
        with open(file_path, mode='r') as blacklist_file:
            return set(line.strip() for line in blacklist_file)

    def initialize_regex_patterns(self, regex_file):
        # Read regex patterns from the file

        with open(regex_file, 'r') as patterns_file:
            for line in patterns_file:
                if line.startswith('#') or not line:
                    continue                        # Skip this line and move to the next one

                line = line.strip()  # Remove leading/trailing whitespace
                if line:
                    self.regex_patterns.append(line)

        #print(self.regex_patterns)

    def procTree(self, proc_file):
        try:

            with open(proc_file, newline='') as csvfile:
                reader = csv.DictReader(csvfile)

                for row in reader:
                    pid = row['PID']
                    ppid = row['PPID']
                    image_file_name = row['ImageFileName']

                    # Create a new node if it doesn't exist
                    if pid not in self.process_tree:
                        self.process_tree[pid] = Node(pid, ppid, image_file_name)

                    # Create a new node for the parent (PPID) if it doesn't exist
                    if ppid not in self.process_tree:
                        self.process_tree[ppid] = Node(ppid, 0, '')

                    # Update the image file name for the current node
                    self.process_tree[pid].image_file_name = image_file_name

                    # Add the current node as a child to its parent
                    add_child(self.process_tree[ppid], self.process_tree[pid])

        except FileNotFoundError:
            print(f"The directory '{proc_file}' does not exist.")
        except PermissionError:
            print(f"You do not have permission to access '{proc_file}'.")
        except Exception as e:
            print(f"An error occurred: {str(e)}")

    def get_parent(self, pid):
        if pid in self.process_tree:
            node = self.process_tree[pid]
            parent_pid = node.ppid
            if parent_pid in self.process_tree:
                return self.process_tree[parent_pid]
        return None

    def find_parent_recursive(self, child_pid):
        # Get the parent node for the given child_pid
        parent_node = self.get_parent(child_pid)

        # Check if a parent node was found
        if parent_node:

            if parent_node.pid not in self.score:
                #print(f"Parent PID: {parent_node.pid}, Image File Name: {parent_node.image_file_name}")

                # Because score keeps track of pid while evidence_bag not. In addition, let the score of the parent is equal to child
                self.score[parent_node.pid] = self.score[child_pid]
                self.collect_evidence(parent_node.pid )
            # If the parent is not services.exe, recursively find its parent
            if parent_node.image_file_name != 'svchost.exe':
                self.find_parent_recursive(parent_node.pid)
        # else:
        #     print(f"No parent found for PID {child_pid}.")

    def malproc(self,node):

        # 1. Should find abnormal parent-child relationship                                                             ==> Done
        # 2. Should find zero parent processes                                                                          ==> Done
        # 3. Should find processes running from weired paths                                                            ==> Done
        # 4. Should hidden/Unlinked processes like processes found in psscan, but not in pslist                         ==> Done
        # 5. When does the process start?                                                                          ==>TBD
        # 6. Should find any process that impersonate known processes and trying to blind in with normal processes      ==> Done

        # 1. Should find abnormal parent-child relationship

        if not node:
            return #suspect_proc

        for child in node.children:
            if not(node.image_file_name in self.normal_proc and child.image_file_name in self.normal_proc[node.image_file_name]):
                # print(node.image_file_name, child.image_file_name)
                self.suspect_proc[child.pid] =self.pslist[child.pid][2:] + ', Suspecious parent-child relationship'

            self.malproc(child)

        return #suspect_proc

    # 2. Should find zero parent processes

    def find_processes_without_parents(self):
        for pid, node in self.process_tree.items():

            if node.ppid not in self.process_tree:
                #root_processes.append(pid)
                if pid == '0':
                    continue
                # print(self.pslist[node.children[0].pid])
                self.suspect_proc[node.children[0].pid] =self.pslist[node.children[0].pid][2:]+ ', Has a zero root parent'

    # 3. Should find processes running from weired paths

    def malpath(self):
        for pid in self.pslist:
            # print(self.process_path)
            if pid in self.process_path:
                path = self.process_path[pid].lower()

                if not ((path in self.normal_paths) or (path in self.normal_paths_x86)):
                        self.suspect_proc[pid] = self.pslist[pid]

    def find_cmd_child(self, path):

        # Iterate through each row in the CSV file and find the pid of a process executed by cmd.exe
        for pid in self.cmdline:
            # print(pid)
            row = self.cmdline[pid]
            args = row[2]
            # print(args)

            # Find the match in the 'Args' column
            if (path in args) and ('cmd.exe' not in args):
                pid = row[0]
                return pid  # Return the PID if a match is found

        return None  # Return None if no match is found for the given path

    def malcmdline(self):
        for pid in self.cmdline:
            # print(pid)
            row = self.cmdline[pid]
            args = row[2]
            pid = row[0]
                # Find the match in the 'Args' column
            # match = re.search(regex_pattern, args)

            # Iterate through each regex pattern
            for regex_pattern in self.regex_patterns:
                match = re.search(regex_pattern, args)
                if match:
                    path_executed = match.group(0)

                    if pid not in self.suspect_cmdlines:
                        self.suspect_cmdlines[pid] = row
                    #matched_paths.append(path_executed)

                    # print(matched_paths)
                    # When cmd.exe executed, it will create a process. search for this process and add it to the list.
                    # If not found in cmdline list, look for it in psscan list
                    # You should also create the same for powershell.exe, wscript.exe, wmiprvse.exe, rundll32.exe, dllhost.exe, ...

                    if 'cmd\.exe' in regex_pattern:
                        pid = self.find_cmd_child(path_executed)
                        if pid:
                            if pid not in self.suspect_cmdlines:
                            # print(f"Path executed: {path_executed}, PID: {pid}")
                                self.suspect_cmdlines[pid] = self.cmdline[pid]
                        else:
                            print(f"No match found for {path_executed}")

    def malcomm(self, blacklist_file_path):

        # Add option for VPN concentrator

        blacklist_addresses = self.initialize_blacklist_addresses(blacklist_file_path)

        # Define the browser processes you want to exclude
        browsers = ["chrome.exe", "firefox.exe", "iexplore.exe", "edge.exe"]  # Add more if needed

        for pid in self.netscan:

            suspect_comm = []
            if len(self.netscan[pid]) > 1:

                for item in self.netscan[pid]:

                    if 'WinStore.App.e' in item:
                        continue
                    row = item.split(',')

                    if (':::0' in row[2]) or ('0.0.0.0:0' in row[2]) or ('::1:0' in row[2]) :
                        continue

                    else:
                        tmp_addr = row[2].split(':')
                        src_ip = tmp_addr[0]
                        local_port = tmp_addr[1]

                        tmp_addr = row[3].split(':')
                        dst_ip = tmp_addr[0]
                        foreign_port = tmp_addr[1]

                        owner = row[6].lower()  # Convert owner to lowercase for case-insensitive comparison

                         # 1. Any process communicating over port 80, 443, or 8080 that is not a browser
                        if foreign_port in ["80", "443", "8080"] and not any(browser in owner for browser in browsers):
                           suspect_comm.append(','.join(row) + ' A process that is not browser using port: '+foreign_port)

                        # 2. Any browser process not communicating over port 80, 443, or 8080
                        elif any(browser in owner for browser in browsers) and foreign_port not in ["80", "443", "8080"]:
                           suspect_comm.append(','.join(row)+ ' A browser communicating over unusual port: '+ foreign_port)

                        # 3. RDP connections (port 3389), particularly if originating from odd IP addresses. External RDP
                        # connections are typically routed through a VPN concentrator. If the src_ip is not from a VPN concentrator, this is malicious
                        elif foreign_port == "3389" and not src_ip.startswith(("*", "::", "0.0.0.0", "127.0.0.1", "172.16.", "192.168.")):
                           suspect_comm.append(','.join(row)+' External IP communicating directly with RDP port')

                        # 4. Connections to unexplained internal or external IP addresses.
                        # External resources like IP reputation services can also provide additional context.
                        elif dst_ip in blacklist_addresses:
                           suspect_comm.append(','.join(row))

                        # 7. Workstation to workstation connections. Workstations don’t typically RDP, map shares, or authenticate to other workstations.
                        # The expected model is workstations communicate with servers. Workstation to workstation connections often uncover lateral movement.
                        elif ((src_ip.startswith("172.16.") and dst_ip.startswith("172.16.")) or \
                                (src_ip.startswith("192.168.") and dst_ip.startswith("192.168."))):
                           suspect_comm.append(','.join(row)+' Workstation to workstation communication')
                        elif foreign_port in ["5985", "5986"] and not dst_ip.startswith(("0.0.0.0", "127.0.0.1")):
                           suspect_comm.append(','.join(row))

            else:
                row = self.netscan[pid][0].split(',')

                if (':::0' in row[2]) or ('0.0.0.0:0' in row[2]) or ('::1:0' in row[2]) :
                    continue

                else:
                    tmp_addr = row[2].split(':')
                    src_ip = tmp_addr[0]
                    local_port = tmp_addr[1]

                    tmp_addr = row[3].split(':')
                    dst_ip = tmp_addr[0]
                    foreign_port = tmp_addr[1]

                    owner = row[6].lower()  # Convert owner to lowercase for case-insensitive comparison

                     # 1. Any process communicating over port 80, 443, or 8080 that is not a browser
                    if foreign_port in ["80", "443", "8080"] and not any(browser in owner for browser in browsers):
                        suspect_comm.append(','.join(row)+ ' A process that is not browser using port: '+foreign_port)

                    # 2. Any browser process not communicating over port 80, 443, or 8080
                    elif any(browser in owner for browser in browsers) and foreign_port not in ["80", "443", "8080"]:
                        suspect_comm.append(','.join(row)+ ' A browser communicating over unusual port: '+ foreign_port)

                    # 3. RDP connections (port 3389), particularly if originating from odd IP addresses. External RDP
                    # connections are typically routed through a VPN concentrator. If the src_ip is not from a VPN concentrator, this is malicious
                    elif foreign_port == "3389" and not src_ip.startswith(("*", "::", "0.0.0.0", "127.0.0.1", "172.16.", "192.168.")):
                        suspect_comm.append(','.join(row)+' External IP communicating directly with RDP port')

                    # 4. Connections to unexplained internal or external IP addresses.
                    # External resources like IP reputation services can also provide additional context.
                    elif dst_ip in blacklist_addresses:
                        suspect_comm.append(','.join(row))

                    # 7. Workstation to workstation connections. Workstations don’t typically RDP, map shares, or authenticate to other workstations.
                    # The expected model is workstations communicate with servers. Workstation to workstation connections often uncover lateral movement.
                    elif ((src_ip.startswith("172.16.") and dst_ip.startswith("172.16.")) or \
                            (src_ip.startswith("192.168.") and dst_ip.startswith("192.168."))):
                        suspect_comm.append(','.join(row)+' Workstation to workstation communication')
                    elif foreign_port in ["5985", "5986"] and not dst_ip.startswith(("0.0.0.0", "127.0.0.1")):
                        suspect_comm.append(','.join(row))

            # suspect_comm is a temp variable collect suspecious procs for every for loop. At the end of every for loop it add the result to suspect_netscan.
            if suspect_comm:
                if pid not in self.suspect_netscan:
                    self.suspect_netscan[pid] = []
                self.suspect_netscan[pid].append(','.join(suspect_comm))

    def malgetsids(self):

        # suspecious_proc_sids = []
        for pid in self.getsids:
            row = self.getsids[pid][0]
            row = row.split(',')
            process = row[2]
            sid = row[3]

            if sid not in self.normal_sids:
                if process in self.normal_sids.get(sid, []):
                    #print(','.join(row), ' --> Malicious Process: System process running with user account')  # Join the values of the row into a CSV formatted string
                    row.append(' --> Malicious Process: System process running with user account')
                    self.suspecious_proc_sids[pid] = ','.join(row)
                else:
                    self.suspecious_proc_sids[pid] = ','.join(row)
                    #print(','.join(row))  # Join the values of the row into a CSV formatted string
            else:
                # Check if the process is not in the list of normal processes for the SID
                if process not in self.normal_sids[sid]:
                    row.append('Malicious Process: Uknown process running with system account')
                    self.suspecious_proc_sids[pid] =','.join(row)
    def malhandles(self):
        # 1. Identify Anomalous Handles:
        #     Look for handles with unusual or unexpected types, such as file handles for system files or handles to uncommon objects.
        #     Pay attention to handles associated with processes that are not part of the normal system configuration.

        # 2. Examine Handle Counts:
        #     Check for processes with an unusually high number of handles. This could indicate a process that is performing a lot of file or resource operations, which may be suspicious.

        # 3. Investigate Suspicious File Handles:
        #     Focus on file handles and look for processes that have handles to sensitive files, such as system binaries or critical configuration files.
        #     Cross-reference file handles with file paths to determine if any files are accessed from suspicious locations.

        # 4. Analyze Network-Related Handles:
        #     Investigate handles related to network resources, such as sockets or network connections. Look for processes communicating with unusual IP addresses or ports.

        # 5. Correlate with Process Information:
        #     Correlate the handles with other process information like process names, PIDs, and parent-child relationships to identify any processes that appear suspicious or out of place.

        # 6. Check for Code Injection:
        #     Look for processes that have handles to memory sections with Execute (X) permissions. This could indicate code injection or process hollowing, common techniques used by malware.

        # 7. Review Timestamps:
        #     Analyze the timestamps associated with handles. Suspicious processes may have handles with timestamps that don't align with the system's normal behavior.

        # 8. Cross-Reference with Known Malware Indicators:
        #     Compare the handles and associated process information with known indicators of compromise (IOCs) and malware signatures to identify matches.

        # 9. Analyze Parent-Child Relationships:
        #     Look for unusual parent-child relationships between processes. Malware often spawns child processes to perform malicious activities, so identifying such relationships can be crucial.

        # 10. Employ YARA Rules:
        #     Use YARA rules to scan the memory and handles for specific patterns or signatures associated with known malware.

        # 11. Leverage Threat Intelligence:
        #     Consult threat intelligence feeds and databases to check if any of the handles or processes are associated with known malware campaigns or threat actors.

        # 12. Behavioral Analysis:
        #     Consider the overall behavior of processes with suspicious handles. Look for patterns that deviate from normal system behavior, such as excessive file manipulation or network communication.

        # Handles represent “pointers” to objects and it is not  unusual for a process to have a pointer to a file it is reading or writing to. However,
        # this is not the common way code is loaded into a process, so it is unusual to see a DLL file referenced in a process handle (recall that loaded
        # DLLs are typically referenced in the Process Environment Block and displayed via the windows.dlllist plugin).
        # CLASSES Key Registry
        # Storing scripts and data in randomly named registry keys has become a common technique for “fileless” malware, so it pays to keep an eye out strange key

        # The interesting handle in the “msiexec.exe” PID 6192 process is a little more obvious. This process only had 16 file handles. If you think about the
        # purpose of “msiexec.exe” (installing .msi files), finding a reference to “flash_installer.msi” likely indicates what was installed on the system.
        # The name and folder location are of particular interest and ultimately led to recovery of the original infection vector.

        # Named pipes can be identified via “File” handles in Volatility and thus when narrowing your focus, you can filter your output on that handle type
        # (this was not done in the example on the slide since output was filtered with grep for the text “pipe”).

        # However, sometimes things stick out with some connected pipes appending IP addresses or process identifiers as part of the pipe name.

        # Malware commonly sets mutants as a way of "marking" a compromised system to prevent reinfection. The malware will first check for the absence of
        # its mutant before performing an infection. During code analysis, reverse engineers can identify what mutant name is set and checked for by the malware.

        pattern = r'\.dll(?!\.mui)'

        for pid in self.handles:
            for row in self.handles[pid]:
                #print(row)
                if re.search(pattern, row[6]):
                    print(row)

                if '.msi' in row[6]:
                    print(row)

                if 'NamedPipe' in row[6]:
                    print(row)

        print('Here will goes handles function')

    def csv_reader(self, csv_file):
    # Create an empty dictionary to store data with "PID" as the index

        # Read and process the CSV file
        filePath = os.path.join(self.app_path, args.p) + '/'+csv_file

        if csv_file == 'netscan.csv':

            try:
                with open(filePath, 'r', newline='') as file:
                    header = next(file).split()
                    header[2] = ' '.join(header[2:4])
                    header[3] = ' '.join(header[4:6])
                    header[4] = header[6]
                    header[5] = header[7]
                    header[6] = header[8]
                    header[7] = header[9]

                    header = header[0:8]
                    pid_index = header.index('Pid')

                    for line in file:
                        row = line.split()

                        if len(row) < 10:
                            row.insert(4, '')

                        row[7] = ' '.join(row[7:])
                        row = row[0:8]

                        if len(row) > pid_index:
                            pid = row[pid_index]
                            if pid not in self.netscan:
                                self.netscan[pid] = []
                        self.netscan[pid].append(','.join(row))

            except FileNotFoundError:
                print(f"File '{csv_file}' not found.")
        else:

            try:
                with open(filePath, 'r', newline='') as csvfile:
                    csvreader = csv.reader(csvfile)
                    header = next(csvreader)  # Read the header

                    # Find the index of the "PID" column
                    pid_index = header.index('PID')

                    path_index = 0
                    if csv_file == 'dlllist.csv':
                        path_index = header.index('Path')

                    if csv_file == 'pslist.csv':
                    # Store the row as a comma-separated string in the dictionary
                        for row in csvreader:
                            if len(row) > pid_index:
                                pid = row[pid_index]
                            self.pslist[pid] = ','.join(row)

                    elif csv_file == 'psscan.csv':
                        for row in csvreader:
                            if len(row) > pid_index:
                                pid = row[pid_index]
                            self.psscan[pid] = ','.join(row)

                    elif csv_file == 'cmdline.csv':
                        for row in csvreader:

                            if len(row) >= 2:
                                if 'process exited' in row[3]:
                                    continue
                                pid = row[pid_index]
                                self.cmdline[pid] = row[1:]

                    elif csv_file == 'dlllist.csv':
                        for row in csvreader:
                            if len(row) > pid_index:
                                pid = row[pid_index]
                                if pid not in self.dlllist:
                                    self.dlllist[pid] = []       # Every process PID has more than one DLL. Keep all dlls for every process in the same pid
                            self.dlllist[pid].append(','.join(row))

                            if len(row) > path_index:
                                path = row[path_index]

                                if '.exe' in path:       #Skipp process path
                                    self.process_path[pid] = path
                                    continue
                                if path not in self.dll_stacking:
                                    self.dll_stacking[path] = 0
                            self.dll_stacking[path] += 1

                    elif csv_file == 'getsids.csv':
                        for row in csvreader:
                            if len(row) == 0:
                                break

                            if len(row) > pid_index:
                                pid = row[pid_index]
                                if pid not in self.getsids:
                                    self.getsids[pid] = []
                            self.getsids[pid].append(','.join(row))

                    elif csv_file == 'handles.csv':
                        filter = ['File', 'Key', 'Mutant']  #

                        for row in csvreader:
                            #print(','.join(row))
                            if  len(row) == 0:
                                continue
                            if row[5] not in filter or row[7] == '':
                                continue

                            if len(row) > pid_index:
                                pid = row[pid_index]
                                if pid not in self.handles:
                                    self.handles[pid] = []       # Every process PID has a lot of handles. Keep all handles for every process in the same pid
                            self.handles[pid].append(row[1:])

            except FileNotFoundError:
                print(f"File '{csv_file}' not found.")

    def collect_evidence(self, pid):
        """This funtion collects artifacts from all memory objects. This how you can print it print(self.artifact_bag['4'][0]['pslist'])"""
        #print(self.artifact_bag['4'][0]['pslist'])
        #print("I will collect artifacts using this funtion!")
        #print('This is the blueprint for this bag: ioc_collection = [pid: {<plugin1>: <row from plugin csv file>}, {<plugin2>: <row from plugin2 csv file>'+'}]')
        #self.evidence_bag[4] = [{'pslist':'0,2876,868,WmiPrvSE.exe,0x8c88b14e7580,10,-,0,False,2018-08-30 13:52:26.000000 ,N/A,Disabled'}]

        # if pid not in self.evidence_bag:
        #     self.evidence_bag[pid] = []

        # Collect evidence from PsList

        # reason = 'High privilege account SID: '

        # if pid in self.getsids:

        #     if len(self.getsids[pid]) > 1:

        #         for item in self.getsids[pid]:

        #             if ('Everyone' in item) or ('Users' in item) or ('This Organization' in item):
        #                 continue

        #             columns = item.split(',')

        #             if 'Local System' == columns[4]:
        #                 reason += ' '.join(columns[3:])

        #             # Don't forget to add the enrichment from malgetsids function.
        #             elif '544' in columns[3]:
        #                 reason += 'The process spawned from a user context with Administrator privileges: '+' '.join(columns[3:])

        #             if pid in self.suspecious_proc_sids:
        #                 reason2 = self.suspecious_proc_sids[pid].split(',')
        #                 if len(reason2) == 6:
        #                     reason += reason2[5]
        ppid = ''
        if pid in self.pslist:

            #print(self.psscan[pid])
            # evidence = {'pslist': self.pslist[pid]}
            #self.evidence_bag[pid].append(evidence)
            row = []
            row.append('pslist')
            columns = self.pslist[pid].split(',')
            row.append(columns[1])

            ppid = columns[2]
            row.append(columns[2])
            row.append(columns[3])

            path = ''
            if pid in self.process_path:
                path = self.process_path[pid]

            # You need to change this list because it is not clean, missing a lot of system files and dlls.
            # if path in self.whitelist_paths and self.score[pid] < 40:
            #     return

            row.append(path)
            row.append(columns[9])

            if pid in self.suspect_proc:
                reason = self.suspect_proc[pid].split(',')[11]
                row.append(reason)

            self.evidence_bag.append(','.join(row))
        # print(pid, ': ',self.evidence_bag)

        # Collect evidence from PsScan
        if (pid in self.psscan) and (pid not in self.pslist):
            #print(self.psscan[pid])
            # evidence = {'psscan': self.psscan[pid]}
            # self.evidence_bag[pid].append(evidence)

            row = []
            row.append('psscan')
            columns = self.psscan[pid].split(',')
            row.append(columns[1])
            row.append(columns[2])
            row.append(columns[3])

            path = ''
            if pid in self.process_path:
                path = self.process_path[pid]

            row.append(path)
            row.append(columns[9])

            # 4. Should hidden/Unlinked processes like processes found in psscan, but not in pslist
            # Hidden processes: Also add more details such as number of threads, parent process, ....
            if row[1] not in self.pslist:
                row.append('Hidden/Unlinked: The process found in psscan, but not in pslist')
            self.evidence_bag.append(','.join(row))

        # Collect evidence from DllList

        if pid in self.dlllist:

            if len(self.dlllist[pid]) > 1:

                for item in self.dlllist[pid]:

                    row =[]
                    row.append('dlllist')
                    # evidence = {'dlllist': self.dlllist[pid]}
                    # self.evidence_bag[pid].append(evidence)

                    columns = item.split(',')

                    if '.exe' in columns[6]:
                        continue

                    # We can add option to use this filtering or not. If we want to find malicious dlls we can use filtering, but
                    # if we want o understand the malcious process capabilities, we ignore filtering becuase used libraries can tell
                    # you the malware can do

                    if columns[6].lower() in self.whitelist_paths:
                        continue

                    if self.dll_stacking[columns[6]] > 2:       # Only keep dlls with low frequency as malicious dlls always rare
                        continue

                    row.append(columns[1])
                    row.append(ppid)
                    row.append(columns[2])
                    row.append(columns[6])
                    row.append(columns[7])
                    row.append('Whitelist the clean DLLs')
                    self.evidence_bag.append(','.join(row))
            else:
                row =[]
                row.append('dlllist')
                # evidence = {'dlllist': self.dlllist[pid]}
                # self.evidence_bag[pid].append(evidence)

                columns = self.dlllist[pid].split(',')
                if self.dll_stacking[columns[6]] > 5:       # Only keep dlls with low frequency as malicious dlls always rare
                    return
                row.append(columns[1])
                row.append(ppid)
                row.append(columns[2])
                row.append(columns[6])
                row.append(columns[7])
                row.append('Whitelist the clean DLLs')
                self.evidence_bag.append(','.join(row))

        # Collect evidence from CmdLine
        if pid in self.cmdline:
            # evidence = {'cmdline': self.cmdline[pid]}
            # self.evidence_bag[pid].append(evidence)

            row =[]
            row.append('cmdline')
            #columns = self.cmdline[pid].split(',')
            row.append(self.cmdline[pid][0])          # Add pid to the list
            row.append(ppid)
            row.append(self.cmdline[pid][1])                    # Add process name

            pattern = r'"?([C|c]:\\[a-zA-Z0-9%\\ \(\)\.]*\.[eE][xX][eE]|[%\\]SystemRoot[%\\][a-zA-Z0-9%\\]*\.exe)\b'
            paths = re.findall(pattern, self.cmdline[pid][2])

            if paths:
                paths = paths[0].strip('"')

            row.append(path)
            row.append('')
            row.append(self.cmdline[pid][2].replace('"', ''))
            self.evidence_bag.append(','.join(row))

        if pid in self.netscan:

            if len(self.netscan[pid]) > 1:

                for item in self.netscan[pid]:
                    row =[]
                    row.append('netscan')
                    # evidence = {'dlllist': self.dlllist[pid]}
                    # self.evidence_bag[pid].append(evidence)

                    columns = item.split(',')
                    row.append(columns[5])
                    row.append(ppid)
                    row.append(columns[6])
                    row.append(columns[1]+' ' + ' --> '.join(columns[2:4])+' '+columns[4])
                    row.append(columns[7])
                    row.append('I Will add info later')
                    self.evidence_bag.append(','.join(row))
            else:
                # evidence = {'netscan': self.netscan[pid]}
                # self.evidence_bag[pid].append(evidence)

                row =[]
                row.append('netscan')
                # evidence = {'dlllist': self.dlllist[pid]}
                # self.evidence_bag[pid].append(evidence)

                columns = self.netscan[pid][0].split(',')
                row.append(columns[5])
                row.append(ppid)
                row.append(columns[6])
                row.append(columns[1]+' ' + ' --> '.join(columns[2:4])+' '+columns[4])
                row.append(columns[7])
                row.append('I Will add info later')

                self.evidence_bag.append(','.join(row))

        if pid in self.getsids:

            if len(self.getsids[pid]) > 1:

                local_system = False
                for item in self.getsids[pid]:

                    if any(keyword in item for keyword in ['Power', 'Logon Session', 'Authentication Authority Asserted Identity', 'PlugPlay', 'Users',
                                                           'This Organization', 'Everyone', 'DcomLaunch', 'High Mandatory Level']):
                        continue

                    row =[]
                    row.append('getsids')
                    columns = item.split(',')

                    if columns[4] == '-':
                        continue
                    row.append(columns[1])
                    row.append(ppid)
                    row.append(columns[2])
                    row.append(columns[3])
                    row.append('')

                    # Don't forget to add the enrichment from malgetsids function.

                    reason = columns[4]
                    if '544' in columns[3]:
                        reason = 'The process spawned from a user context with high privileges account: ' + reason

                    if pid in self.suspecious_proc_sids:
                        reason2 = self.suspecious_proc_sids[pid].split(',')
                        if len(reason2) == 6:
                            reason = reason2[5] +': '+ reason

                    row.append(reason)
                    self.evidence_bag.append(','.join(row))

    def create_SuperMemoryParser(self):

        header = ['Source Name', 'PID', 'Process Name', 'Path', 'Timestamps', 'Long Description']

        with open('SuperMemoryParser.csv', 'w') as mparser:
            mparser.write('\n'.join(self.evidence_bag))

    def malicious_weight(self):

        if self.pslist:
            for pid in self.pslist:
                weight = 0
                if pid in self.suspect_proc:
                    # if pid =='9036':
                    #     print(f"Found {pid} in suspect_proc: {self.suspect_proc[pid]}")
                    weight += 10

                    if 'Has a zero root parent' in self.suspect_proc[pid]:
                        weight += 30

                if pid in self.suspecious_proc_sids:
                    # if pid =='9036':
                    #     print(f"Found {pid} in suspecious_proc_sids: {self.suspecious_proc_sids[pid]}")
                    weight += 10

                if pid in self.suspect_cmdlines:
                    # if '9036' in self.suspect_cmdlines[pid]:
                    #     print(f"Found {pid} in suspect_cmdlines: {self.suspect_cmdlines[pid]}")
                    weight += 10

                if pid in self.suspect_netscan:
                    # if pid =='9036':
                    #     print(f"Found {pid} in suspect_netscan: {self.suspect_netscan[pid]}")
                    weight += 10

                # print(self.baseline_proc['1096'])
                if pid in self.baseline_proc:
                    # if pid =='9036':
                    #     print(f"Found {pid} in baseline_proc: {self.baseline_proc[pid]}")
                    weight += 10

                if weight > 30:
                    self.score[pid] = weight

# Function to add a child node to a parent node
def add_child(parent, child):
    parent.children.append(child)

# Function to recursively print the tree with '*' depth

def print_process_tree(node, depth=0):

    #     # Print the node's information with depth represented by '*' characters
    #     print("*" * depth + ' '*(12-depth)+ f"{node.pid}, {node.ppid}, {node.image_file_name}")
    #     # Recursively print children

    if node:
            # Print the header only for the root node
        if depth == 0:
            print(f"{'':<12}{'PID':<12}{'PPID':<12}{'Image File Name'}")
        # Check if the node is not the root (0, 0)
        if node.pid != '0':
            # Print the node's information with depth represented by '*' characters
            print("*" * depth + ' ' * (12 - depth) + f"{node.pid:<12}{node.ppid:<12}{node.image_file_name}")
        # Recursively print children
        for child in node.children:
            print_process_tree(child, depth + 1)

def get_application_path():
    try:
        if getattr(sys, 'frozen', False):
            application_path = os.path.dirname(os.path.realpath(sys.executable))
        else:
            application_path = os.path.dirname(os.path.realpath(__file__))
        if "~" in application_path and os_platform == "windows":
            # print "Trying to translate"
            # print application_path
            application_path = win32api.GetLongPathName(application_path)
        #if args.debug:
        #    logger.log("DEBUG", "Init", "Application Path: %s" % application_path)
        return application_path
    except Exception as e:
        print("Error while evaluation of application path")
        traceback.print_exc()
        if args.debug:
            sys.exit(1)

def main(memory):

    """
    Argument parsing function
    :return:
    """
    # for pid in memory.score:
    #     print(pid, ': ', memory.score[pid])

    # child_pid = '8260'  # Replace with the PID of the child you want to find the parent for
    # # parent_node = memory.get_parent(child_pid)

    # memory.find_parent_recursive(child_pid)
    # parent_node = memory.get_parent(child_pid)

    # while parent_node.image_file_name != 'svchost.exe':

    #     parent_node = memory.get_parent(child_pid)

    #     if parent_node:
    #         print(f"Parent PID: {parent_node.pid}, Image File Name: {parent_node.image_file_name}")
    #     else:
    #         print(f"No parent found for PID {child_pid}.")

    #     child_pid = parent_node.pid

    pid_list = [key for key in memory.score]

    for pid in pid_list:
        memory.collect_evidence(pid)
        #print(pid, ': ', memory.score[pid])
        # parent = memory.pslist[pid].split(',')[2]

        memory.find_parent_recursive(pid)

        # if parent not in memory.score:
        #     memory.collect_evidence(parent)

    memory.create_SuperMemoryParser()

    while True:

        print("Available plugins to explore memory data:")
        for index, command in enumerate(memory.plugins, start=1):
            print(f"{index}. {command}")

        print("0. Exit")

        try:
            choice = int(input("Enter the plugin number you want to select (or 0 to quit): "))
            if choice > len(memory.plugins):
                print("\nInvalid input. Please enter a valid number.")
                continue
            if choice == 0:
                print(memory.score)
                break
            selected_command = memory.plugins[choice - 1]
            corresponding_file = memory.csv_files[choice - 1]
            print(f"Selected command: {selected_command}")
            print(f"Corresponding CSV file: {corresponding_file}")

            if selected_command == 'pslist':
                for pid in memory.pslist:
                    print(memory.pslist[pid])

            if selected_command == 'psscan':
                for pid in memory.psscan:
                    print(memory.psscan[pid])

            if selected_command == 'pstree':
                print_process_tree(memory.process_tree['0'])

            if selected_command == 'dlllist':
                for pid in memory.dlllist:
                    print(memory.dlllist[pid])

            if selected_command == 'cmdline':
                for pid in memory.cmdline:
                    print(memory.cmdline[pid])

            if selected_command == 'netscan':
                for pid in memory.netscan:
                    print(memory.netscan[pid])

            if selected_command == 'getsids':
                for pid in memory.getsids:
                    print(memory.getsids[pid])

            print("1. Display only suspecious processes")
            print("2. Continue with full list")
            print("0. Back")

            try:
                choice = int(input("Select one option (or 0 go back): "))
                if choice == 0:
                    continue

                elif choice == 1:
                    while True:
                        print("Display only suspecious processes:")
                        for index, command in enumerate(memory.analyze, start=1):
                            print(f"{index}. {command}")
                        print("0. Back")

                        try:
                            choice = int(input("Enter the number corresponding to the analysis you want to select (or 0 go back): "))

                            if choice == 0:
                                break

                            elif choice <= len(memory.analyze):
                                if choice == 1:
                                    #suspect_proc = memory.malproc(memory.process_tree['4'])
                                    for pid in memory.suspect_proc:
                                        print(memory.suspect_proc[pid])
                                    print('\n\nThe above processes are more likely the most suspecious processes. Select one of them by PID\n\n')

                                elif choice == 3:
                                    for pid in memory.suspect_cmdlines:
                                        print(memory.suspect_cmdlines[pid])

                                elif choice == 5:
                                    print('\nThis function have not been implemted, please do it!')
                                    continue

                                    suspect_proc = memory.malcomm('blacklist.txt')
                                    print('\n'.join(suspect_proc))

                                elif choice == 6:
                                    for pid in memory.suspect_netscan:
                                        print(memory.suspect_netscan[pid])

                                elif choice == 7:
                                    for pid in memory.baseline_proc:
                                        print(memory.baseline_proc[pid].replace('"', ''))

                                elif choice == 8:
                                    memory.malgetsids()

                                    for pid in memory.suspecious_proc_sids:
                                        print(memory.suspecious_proc_sids[pid])

                                while True:
                                    # Prompt for the PID to search for
                                    pid_to_search = input("\n\nEnter the suspecious process PID that you want to collect as evidence (or type 'back' to go back to plugins selection): ")

                                    if pid_to_search.lower() == 'back':
                                        break
                                    memory.collect_evidence(pid_to_search)
                                            #print(memory.pslist[pid_to_search])

                            else:
                                print("Invalid choice. Please select a valid number.")

                        except ValueError:
                            print("Invalid input. Please enter a valid number.")

                elif choice == 2:
                    while True:
                        # Prompt for the PID to search for
                        pid_to_search = input("\n\nEnter the suspecious process PID that you want to collect as evidence (or type 'back' to go back to plugins selection): ")

                        if pid_to_search.lower() == 'back':
                            break
                        memory.collect_evidence(pid_to_search)
                                #print(memory.pslist[pid_to_search])
                        #print(memory.evidence_bag)
                else:
                    print("Invalid choice. Please select a valid number.")

            except ValueError:
                print("Invalid input. Please enter a valid number.")

            # for key, value in memory.evidence_bag.items():
            #     formatted_output = []
            #     for item in value:
            #         for entry_type, entry_data in item.items():
            #             if entry_type == 'dlllist':
            #                 formatted_output.append(f'{entry_type}:')
            #                 formatted_output.extend([f'    {line}' for line in entry_data])

            #             elif entry_type == 'netscan':
            #                 formatted_output.append(f'{entry_type}:')
            #                 formatted_output.extend([f'    {line}' for line in entry_data])
            #             else:
            #                 formatted_output.append(f'{entry_type}: {entry_data}')
            #     print("\n".join(formatted_output))
            #     print('\n')

        except ValueError:
            print("Invalid input. Please enter a valid number.")

if __name__ == "__main__":

    # Argument parsing

    # Parse Arguments
    parser = argparse.ArgumentParser(description='AutVol3 - Simple Memoray Image Analyzer')
    parser.add_argument('-p', help='Path to memory image and csv files like pslist, netscan ...', metavar='path', default='')
    parser.add_argument('--version', action='store_true', help='Shows welcome text and version of AutoVol3, then exit', default=False)

    # Add option for baseline
    # Add option for image profile if using vol2
    # Add option for blacklist IPs
    # Add option for chosing memory image file
    # Add option to add VPN concentrator

    args = parser.parse_args()

    if not args.p:
        print('Must specify memory image path!')
        parser.print_help()
        sys.exit(1)

    autovol3 = AutoVol3(args)

    # Show version
    if args.version:
        sys.exit(0)
    #suspect_proc = autovol3.malcomm('blacklist.txt')
    #autovol3.malgetsids()
    main(autovol3)

    # print('This is the last push')


from math import sqrt

def isPrime(num):
	for i in range(2, int(sqrt(num)) + 1):
		if num % i == 0:
			return False
	return True

i = 1
count = 2

while True:
	n1 = 6 * i + 1
	n2 = 6 * i - 1
	i += 1
	if isPrime(n1):
		count += 1
		if count == 10001:
			print(n1)
			break
	if isPrime(n2):
		count += 1
		if count == 10001:
			print(n2)
			break


# Se tiene una cantidad de números dada donde hay varios primos determinar
# si el primo 2 y el primo 3 de acuerdo al orden de entrada si son consecutivos.
# Son consecutivos si entre los dos no hay otro número primo
cantidad=int(input("Digite la cantidad de datos, debe contener al menos 3 primos"))
primo2=primo3=0
cantidad_primos=0
consecutivo=True
contador=1
while contador<=cantidad:
    num=int(input(f"digite el dato{contador}->"))
    divisores=0
    z=1
    while z<num:
        if num%z==0:
            divisores+=1
        z+=1
    if divisores==2:
        cantidad_primos+=1
        if cantidad_primos==2:
            primo2=num
        elif cantidad_primos==3:
            primo3=num
    contador+=1
print("segundo primo", primo2)
print("tercer primo", primo3)


import gym

class GymSpec:
    def __init__(self, name, env_id):
        self.name = name
        self.env_id = env_id

GYM_ENVS = [
    GymSpec('gym_CartPole-v0', 'CartPole-v0'),
    GymSpec('gym_CartPolev-1', 'CartPole-v1'),
]

def gym_env_by_name(name):
    for cfg in GYM_ENVS:
        if cfg.name == name:
            return cfg
    raise Exception('Unknown Gym env')

def make_gym_env(env_name, cfg, **kwargs):
    mujoco_spec = gym_env_by_name(env_name)
    env = gym.make(mujoco_spec.env_id)
    return env


import shutil
import gc
import copy
import numpy
import random
import cv2
from PIL import Image, ImageDraw
import os
from functools import partial
from scipy.ndimage.filters import gaussian_filter
import time
import pickle
import re
from sklearn import preprocessing
import scipy.io as sio
from Att_BiLSTM_training import test_att_BiLSTM
from keras.models import *
from statistics import mode
from scipy import interpolate
from scipy.io import loadmat
import sys
import matplotlib.pyplot as plt
from SMGaccessSample import GestureSample
from scipy.special import softmax

'''
load SMG training dataset
'''
def loader_SMG(parsers):

    # start counting tim
    time_tic = time.time()

    #counting feature number
    HMM_state_feature_count = 0
    n_HMM_state_feature_count = 0

    #how many joints are used
    njoints = parsers['njoints']

    used_joints = parsers['used_joints']

    #HMM temporal steps
    Time_step_NO = parsers['STATE_NUM']#3

    #feature dimension of the LSTM input
    featurenum = int((njoints*(njoints-1)/2 + njoints**2)*3)

    #get sample list
    Sample_list = sorted(os.listdir(parsers['data']))

    dictionaryNum = parsers['class_count']*parsers['STATE_NUM']+1

    '''pre-allocating the memory '''
    #gesture features
    Feature_all_states = numpy.zeros(shape=(400000, featurenum), dtype=numpy.float32)
    Targets_all_states = numpy.zeros(shape=(400000, dictionaryNum), dtype=numpy.uint8)

    n_Feature_all_states = numpy.zeros(shape=(400000, featurenum), dtype=numpy.float32)
    n_Targets_all_states = numpy.zeros(shape=(400000, dictionaryNum), dtype=numpy.uint8)

    # HMM pror and transition matrix
    Prior = numpy.zeros(shape=(dictionaryNum))
    Transition_matrix = numpy.zeros(shape=(dictionaryNum,dictionaryNum))

    #start traversing samples
    for sampleID in Sample_list[0:35]:

        print("\t Processing file " + str(sampleID))

        smp=GestureSample(os.path.join(parsers['data'],sampleID),sampleID)

        gesturesList=smp.getGestures()

        Transition_matrix[-1, -1] += smp.getNumFrames()

        MG_flag = smp.getMGFlag()

        '''traverse all ges in samples'''
        #get the skeletons of the gesture
        for ges_info in gesturesList:
            gestureID, startFrame, endFrame = ges_info
            # print(endFrame - startFrame)
            '''
            1. extract skeleton features of ges
            '''
            Skeleton_matrix, valid_skel = Extract_feature_UNnormalized(smp, used_joints, startFrame, endFrame)
            # to see we actually detect a skeleton:

             ### extract the features according to the CVPR2014 paper
            Feature = Extract_feature_Realtime(Skeleton_matrix, njoints)

            '''generatre the corresponding labels'''
            sample_label = extract_HMMstate_label(Time_step_NO, Feature.shape[0], dictionaryNum, gestureID)

            #assign seg_length number of features to current gesutre
            Feature_all_states[HMM_state_feature_count:HMM_state_feature_count + Feature.shape[0], :] = Feature
            #assign seg_length number of labels to corresponding features
            Targets_all_states[HMM_state_feature_count:HMM_state_feature_count + Feature.shape[0], :] = sample_label
            #update feature count
            HMM_state_feature_count = HMM_state_feature_count + Feature.shape[0]

            '''
            2. extract skeleton features of non ges
            '''
            '''non movement before 5 frames'''
            if 1 not in MG_flag[startFrame-5:startFrame]:
                n_startFrame = startFrame-5
                n_endFrame = startFrame

                Skeleton_matrix, valid_skel = Extract_feature_UNnormalized(smp, used_joints, n_startFrame, n_endFrame)

                Feature = Extract_feature_Realtime(Skeleton_matrix, njoints)

                '''generatre the corresponding labels'''
                #generatre the corresponding labels
                sample_label = extract_nonActionlabel(Feature.shape[0], dictionaryNum)
                #assign seg_length number of features to current gesutre
                n_Feature_all_states[n_HMM_state_feature_count:n_HMM_state_feature_count + Feature.shape[0], :] = Feature
                #assign seg_length number of labels to corresponding features
                n_Targets_all_states[n_HMM_state_feature_count:n_HMM_state_feature_count + Feature.shape[0], :] = sample_label
                #update feature count
                n_HMM_state_feature_count = n_HMM_state_feature_count + Feature.shape[0]

            '''non movement after 5 frames'''

            if 1 not in MG_flag[endFrame:endFrame+5]:
                n_startFrame = endFrame
                n_endFrame = endFrame+5

                Skeleton_matrix, valid_skel = Extract_feature_UNnormalized(smp, used_joints, n_startFrame, n_endFrame)
                Feature = Extract_feature_Realtime(Skeleton_matrix, njoints)

                '''generatre the corresponding labels'''
                #generatre the corresponding labels
                sample_label = extract_nonActionlabel(Feature.shape[0], dictionaryNum)
                #assign seg_length number of features to current gesutre
                n_Feature_all_states[n_HMM_state_feature_count:n_HMM_state_feature_count + Feature.shape[0], :] = Feature
                #assign seg_length number of labels to corresponding features
                n_Targets_all_states[n_HMM_state_feature_count:n_HMM_state_feature_count + Feature.shape[0], :] = sample_label
                #update feature count
                n_HMM_state_feature_count = n_HMM_state_feature_count + Feature.shape[0]

            '''
            3. extract HMM transition info of this ges
            '''

            for frame in range(endFrame-startFrame+1-4):
                # print(gestureID)
                state_no_1,state_no_2 = HMMmatrix(gestureID, frame, endFrame-startFrame+1-4, Time_step_NO)
                # print(state_no_2)
                ## we allow first two states add together:
                Prior[state_no_1] += 1
                Transition_matrix[state_no_1, state_no_2] += 1
                Transition_matrix[-1, -1] -= 1
                if frame<2:
                    Transition_matrix[-1, state_no_1] += 1
                    Prior[-1] += 1
                if frame> (endFrame-startFrame+1-4-2):
                    Transition_matrix[state_no_2, -1] += 1
                    Prior[-1] += 1

    Feature_all_states = Feature_all_states[:HMM_state_feature_count, :]
    #assign seg_length number of labels to corresponding features
    Targets_all_states = Targets_all_states[:HMM_state_feature_count, :]

    print('gesture')
    print(HMM_state_feature_count)

    n_Feature_all_states = n_Feature_all_states[:n_HMM_state_feature_count, :]
    #assign seg_length number of labels to corresponding features
    n_Targets_all_states = n_Targets_all_states[:n_HMM_state_feature_count, :]
    print('non gesture')
    print(n_HMM_state_feature_count)

    num_samples = 20000
    idx = numpy.random.randint(0,len(n_Feature_all_states),size=(num_samples))
    # print(idx)
    n_Feature_all_states = n_Feature_all_states[idx]
    n_Targets_all_states = n_Targets_all_states[idx]
    # save the feature file:
    Feature_all_states = numpy.concatenate((Feature_all_states, n_Feature_all_states))
    Targets_all_states = numpy.concatenate((Targets_all_states, n_Targets_all_states))
    HMM_state_feature_count +=num_samples

    Feature_all,Targets_all,SK_normalizationfilename = process_feature(parsers,Feature_all_states,Targets_all_states,HMM_state_feature_count)

    print ("Processing data done with consuming time %d sec" % int(time.time() - time_tic))

    return Prior, Transition_matrix, Feature_all,Targets_all,SK_normalizationfilename

'''
sparse data list
'''
def process_feature(parsers, Feature_all,Targets_all,action_feature_count):

    # save the feature file:
    print ('total training samples: ' + str(action_feature_count))
    Feature_all = Feature_all[0:action_feature_count, :]
    Targets_all = Targets_all[0:action_feature_count, :]

    #random the samples
    rand_num = numpy.random.permutation(Feature_all.shape[0])
    Feature_all = Feature_all[rand_num]
    Targets_all  = Targets_all[rand_num]

    #[train_set_feature_normalized, Mean1, Std1]  = preprocessing.scale(train_set_feature)
    scaler = preprocessing.StandardScaler().fit(Feature_all)
    Mean1 = scaler.mean_
    Std1 = scaler.scale_
    Feature_all = normalize(Feature_all,Mean1,Std1)

    # save the normalization files
    SK_normalizationfilename = parsers['outpath']+ parsers['experi_ID'] +'SK_normalization.pkl'

    f = open(SK_normalizationfilename,'wb')
    pickle.dump( {"Mean1": Mean1, "Std1": Std1 },f)
    f.close()

    return Feature_all,Targets_all, SK_normalizationfilename

'''
using DNN to get HMM emission probability
'''
def emission_prob(modelname, parsers, Feature, Mean1, Std1,best_threshold,modeltype,AES=True,mu=0.0,lamd=1.0):
    Feature_normalized = normalize(Feature, Mean1, Std1)
    #print Feature_normalized.max()
    #print Feature_normalized.min()

    # feed to  Network
    y_pred_4 = test_att_BiLSTM(modelname, parsers, Feature_normalized, best_threshold)
    #y_pred_4 = test_torch_BiLSTM(modelname, parsers, Feature_normalized,best_threshold, modeltype, iflog)

    if AES:

        y_pred_4 = y_pred_4.T
        observ_likelihood = y_pred_4
        # print(y_pred_4)
        atten_value = y_pred_4[:-1, :]* y_pred_4[:-1, :]/9
        # print(atten_value)softmax(x, axis=0)
        SFMX = softmax(atten_value, axis=0)
        #
        # print(SFMX)
        observ_likelihood[:-1, :] = SFMX* y_pred_4[:-1, :]*mu + y_pred_4[:-1, :]
        # print(observ_likelihood)
        observ_likelihood= numpy.log(observ_likelihood)
        # print(observ_likelihood)
        observ_likelihood[-1, :] = observ_likelihood[-1, :] *lamd

    else:
        observ_likelihood = numpy.log(y_pred_4.T)

    return observ_likelihood

'''
save features
'''
def datasaver(parsers, Prior, Transition_matrix,Feature_all,Targets_all):

    '''define name'''

    Prior_Transition_matrix_filename = parsers['outpath']+ parsers['experi_ID'] + str(parsers['STATE_NUM']) + 'Prior_Transition_matrix.mat'
    Feature_filename = parsers['outpath'] + parsers['experi_ID'] + str(parsers['STATE_NUM']) + 'feature'+ str(parsers['featureNum']) +'Feature_all.pkl'

    '''HMM transition state'''
    #save HMM transition matrix
    sio.savemat( Prior_Transition_matrix_filename, {'Transition_matrix':Transition_matrix, 'Prior': Prior})

    '''feature storage'''
    # save the skeleton file:
    f = open(Feature_filename, 'wb')
    pickle.dump({"Feature_all": Feature_all, "Targets_all": Targets_all }, f,protocol=4)
    f.close()

    return Prior_Transition_matrix_filename, Feature_filename

'''
package Parameters into one file
'''
def packagePara(parsers, model1name, norm_name,HMM_file):

    Paras = {'model1':model1name,
             'norm_para':norm_name,
             'HMM_model':HMM_file,
             }
    path = parsers['outpath'] + parsers['experi_ID'] + '/Paras.pkl'
    afile = open(path, 'wb')
    pickle.dump(Paras, afile)
    afile.close()

'''
test result
'''
def tester_SMG(parsers,best_threshold):

    used_joints = parsers['used_joints']
    dictionaryNum = parsers['class_count']*parsers['STATE_NUM']+1

    MODEL4 = load_model(parsers['outpath']+ parsers['experi_ID'] +'/my_model.h5')
    #print(ges_info_list)
    correct_count = 0.0
    total_count = 0.0
    acc_total = 0.0

    time_tic = time.time()
    datacheck = []

    path = parsers['outpath']+ parsers['experi_ID'] + '/Paras.pkl'
    file2 = open(path, 'rb')
    Paras = pickle.load(file2)
    file2.close()

    ### load the pre-store normalization constant
    f = open(Paras['norm_para'],'rb')
    SK_normalization = pickle.load(f)
    Mean1 = SK_normalization ['Mean1']
    Std1 = SK_normalization['Std1']

    ## Load networks
    modelname1 = Paras['model1']

    ## Load Prior and transitional Matrix
    dic=sio.loadmat(Paras['HMM_model'])
    Transition_matrix = dic['Transition_matrix']

    Transition_matrix[-1, -1]= Transition_matrix[-1, -1]

    Prior = dic['Prior']
    ## Load trained networks
    njoints = parsers['njoints']

    #get sample list
    Sample_list = sorted(os.listdir(parsers['data']))

    total_F1 = 0.0
    total_acc = 0.0
    total_rec = 0.0

    recall_len = 0
    acc_len = 0
    correct_count = 0.0
    threshold_alpha =  parsers['threshold_alpha']
    #start traversing samples
    for sampleID in Sample_list[35:]:

        print(sampleID)
        time_single = time.time()

        '''1 extract skeleton features of this ges'''
        '''process the gesture parts'''
        smp=GestureSample(os.path.join(parsers['data'],sampleID),sampleID)

        gesturesList=smp.getGestures()

        frame_count =smp.getNumFrames()

        MG_flag = smp.getMGFlag()

        Skeleton_matrix, valid_skel = Extract_feature_UNnormalized(smp, used_joints,  1, frame_count - 1)
        # to see we actually detect a skeleton:

         ### extract the features according to the CVPR2014 paper
        Feature = Extract_feature_Realtime(Skeleton_matrix, njoints)
        #ratio = 0.8
        #visiblenumber = int(ratio* (frame_count))

        # sample_feature1 = copy.copy(sample_feature)
        # observ_likelihood1 = emission_prob(modelname1, parsers, sample_feature1, Mean1, Std1, best_threshold, parsers['netmodel'],False)

        sample_feature2 = copy.copy(Feature)
        log_observ_likelihood1 = emission_prob(MODEL4, parsers, sample_feature2, Mean1, Std1, best_threshold, parsers['netmodel'], True,parsers['mu'],parsers['lambda'] )#parsers['AES'])

        log_observ_likelihood1[-1, 0:5] = 0
        log_observ_likelihood1[-1, -5:] = 0
        #
        log_observ_likelihood = log_observ_likelihood1#[:,mask]# + log_observ_likelihood1
        #print("\t Viterbi path decoding " )
        #do it in log space avoid numeric underflow
        [path, _, global_score] = viterbi_path_log(numpy.log(Prior), numpy.log(Transition_matrix), log_observ_likelihood)

        pred_label, begin_frame, end_frame, Individual_score, frame_length = viterbi_colab_MES(path, global_score, state_no = parsers['STATE_NUM'], threshold=-3, mini_frame=15, cls_num = parsers['class_count'])#viterbi_colab_clean_straight(parsers['STATE_NUM'], parsers['class_count'], path, global_score,)

        MG_flag = numpy.zeros(smp.getNumFrames())

        #
        # if True:
        #     # global_score=global_score[:,0:1000]
        #     im  = imdisplay(global_score)
        #     plt.imshow(im, cmap='Greys')## cmap='gray')
        #     plt.plot(range(global_score.shape[-1]), path, color='c',linewidth=2.0)
        #     plt.xlim((0, 2000))#global_score.shape[-1]))
        #     plt.ylim((0, 100))#global_score.shape[-2]))
        #     # plot ground truth
        #     for gesture in gesturesList:
        #     # Get the gesture ID, and start and end frames for the gesture
        #         gestureID,startFrame,endFrame=gesture
        #         frames_count = numpy.array(range(startFrame, endFrame+1))
        #         pred_label_temp = ((gestureID-1) *5+3) * numpy.ones(len(frames_count))
        #         plt.plot(frames_count, pred_label_temp, color='r', linewidth=5.0)
        #
        #     # plot clean path
        #     for i in range(len(begin_frame)):
        #         frames_count = numpy.array(range(begin_frame[i], end_frame[i]+1))
        #         pred_label_temp = ((pred_label[i]-1) *5+3) * numpy.ones(len(frames_count))
        #         plt.plot(frames_count, pred_label_temp, color='b', linewidth=2.0)
        #
        #     plt.show()
        # #

        pred_len = len(pred_label)

        # if True:
        #     # fig = plt.figure()
        #     # fig.set_facecolor("antiquewhite")
        #     im  = imdisplay(global_score)
        #     plt.imshow(im, cmap = 'antiquewhite')
        #     plt.plot(range(global_score.shape[-1]), path, color='c',linewidth=2.0)
        #     plt.xlim((0, 1000))#global_score.shape[-1]))
        #     plt.ylim((0, 100))#global_score.shape[-2]))
        #     # plot ground truth
        #     for gesture in gesturesList[:10]:
        #     # Get the gesture ID, and start and end frames for the gesture
        #         gestureID,startFrame,endFrame=gesture
        #         frames_count = numpy.array(range(startFrame, endFrame+1))
        #         pred_label_temp = ((gestureID-1) *10 +5) * numpy.ones(len(frames_count))
        #         plt.plot(frames_count, pred_label_temp, color='r', linewidth=5.0)
        #
        #     # plot clean path
        #     for i in range(len(begin_frame)):
        #         frames_count = numpy.array(range(begin_frame[i], end_frame[i]+1))
        #         pred_label_temp = ((pred_label[i]-1) *10 +5) * numpy.ones(len(frames_count))
        #         plt.plot(frames_count, pred_label_temp, color='b', linewidth=2.0)
        #
        #     plt.show()

        current_correct_count = 0.0

        for ges in gesturesList:
            gt_label, gt_begin_frame, gt_end_frame = ges
            for pred_i in range(len(begin_frame)):
                # print(pred_i)
                if pred_label[pred_i] == gt_label:
                    alpha = (min(end_frame[pred_i],gt_end_frame)-max(begin_frame[pred_i],gt_begin_frame))/(max(end_frame[pred_i],gt_end_frame)-min(begin_frame[pred_i],gt_begin_frame))
                    # print(pred_i)
                    if alpha> threshold_alpha:
                        # print(pred_label)
                        numpy.delete(pred_label, pred_i)
                        numpy.delete(begin_frame, pred_i)
                        numpy.delete(end_frame, pred_i)
                        #print(pred_label)
                        correct_count +=1
                        current_correct_count +=1
                        break

        recall_len =recall_len + len(gesturesList)
        acc_len = acc_len + pred_len
        print(len(gesturesList))
        print(pred_len)
        # pred_label
        '''recall'''
        current_recall = current_correct_count/len(gesturesList)
        '''precise'''
        current_precision = current_correct_count/pred_len
        current_F1_score = 2*current_recall* current_precision/(current_recall + current_precision+0.0000001)
        print("Used time %d sec, processing speed %f fps, F1 score%f, Recall %f, precision%f" %(int(time.time() - time_single),frame_count/float(time.time() - time_single),current_F1_score, current_recall, current_precision))

    '''recall'''
    recall = correct_count/recall_len
    '''precise'''
    precision = correct_count/acc_len

    total_acc += precision
    total_rec += recall
    F1_score = 2*recall* precision/(recall + precision+0.0000001)
    total_F1 += F1_score

    print ("Processing testing data done with consuming time %d sec" % int(time.time() - time_tic))

    #F1_total = total_F1/len(Sample_list[35:])
    print(parsers['experi_ID']+". The rec for this prediction is " + "{:.12f}".format(total_rec))
    print(parsers['experi_ID']+". The acc for this prediction is " + "{:.12f}".format(total_acc))
    print(parsers['experi_ID']+". The score for this prediction is " + "{:.12f}".format(total_F1))
    print('mu'+str(parsers['mu'])+'lambda'+str(parsers['lambda']))
    numpy.savetxt(parsers['outpath']+ parsers['experi_ID'] +'_score_'+ str(acc_total) +'.txt', [])

def extract_temporal_movingPose(skeleton_all, frame_compens , LSTM_step, njoints):

    frame_count =  skeleton_all.shape[0] - frame_compens*2

    feature_all = Extract_moving_pose_Feature(skeleton_all, njoints)

    feature_dim = feature_all.shape[1]

    feature_n  = numpy.zeros(shape=(frame_count, feature_dim*LSTM_step))

    for frame in range(frame_count):

        for step in range(LSTM_step):

            feature_n[frame, step*feature_dim:(step+1)*feature_dim] = feature_all[frame+step,:]

    return feature_n, feature_n.shape[0]

def extract_HMMstate_label(STATE_NO, action_count, dictionaryNum, gestureID):
    # label the features
    target = numpy.zeros(shape=(action_count, dictionaryNum))
    # HMM states force alignment
    for i in range(STATE_NO):
        # get feature index of the current time step
        begin_feature_index = int(numpy.round(action_count * i / STATE_NO) + 1)
        end_feature_index = int(numpy.round(action_count * (i + 1) / STATE_NO))
        # get feature length of the current time step
        seg_length = end_feature_index - begin_feature_index + 1
        labels = numpy.zeros(shape=(dictionaryNum, 1))
        # assign the one hot labels
        try:
            labels[ i + STATE_NO*gestureID] = 1
        except:
            print(labels.shape)
            print( i + STATE_NO*gestureID)

        target[begin_feature_index-1:end_feature_index,:] = numpy.tile(labels.T, (seg_length, 1))
    return target

def extract_nonActionlabel (action_count, dictionaryNum):
    target_n = numpy.zeros(shape=(action_count, dictionaryNum))
    target_n[:,-1] = 1
    return target_n

'''
record HMM transition matrix
'''
def HMMmatrix(gestureID, frame, frame_count,STATE_NO):
    state_no_1 = numpy.floor(STATE_NO*(frame*1.0/(frame_count+3)))
    state_no_1 = int(state_no_1+STATE_NO*(gestureID))
    state_no_2 = numpy.floor(STATE_NO*((frame+1)*1.0/(frame_count+3)))
    state_no_2 = int(state_no_2+STATE_NO*(gestureID))
    return state_no_1,state_no_2

def normalize(Data, Mean, Std):
    # print(Data.shape)
    # print(Mean.shape)
    Data -= Mean
    Data /= Std
    return Data

def Extract_feature_UNnormalized(smp, used_joints, startFrame, endFrame):
    """
    Extract original features
    """
    frame_num = 0
    Skeleton_matrix  = numpy.zeros(shape=(endFrame-startFrame+1, len(used_joints)*3))

    for numFrame in range(startFrame,endFrame+1):
        # Get the Skeleton object for this frame
        skel=smp.getSkeleton(numFrame)
        for joints in range(len(used_joints)):
            # print((skel.joins[used_joints[joints]][0]))
            # print(Skeleton_matrix[frame_num, joints*3: (joints+1)*3])
            print(skel.joins[used_joints[joints]][0])
            Skeleton_matrix[frame_num, joints*3: (joints+1)*3] =skel.joins[used_joints[joints]][0]

        frame_num += 1

    if numpy.allclose(sum(sum(numpy.abs(Skeleton_matrix))),0):
        valid_skel = False
    else:
        valid_skel = True

    return Skeleton_matrix, valid_skel

def Extract_moving_pose_Feature(Skeleton_matrix_Normalized, njoints):

    #pose
    F_pose = Skeleton_matrix_Normalized

    #velocity
    F_velocity = Skeleton_matrix_Normalized[2:,:] - Skeleton_matrix_Normalized[0:-2,:]

    #accelerate
    F_accelerate = Skeleton_matrix_Normalized[4:,:] + Skeleton_matrix_Normalized[0:-4,:] - 2 * Skeleton_matrix_Normalized[2:-2,:]

    #absolute pose
    FeatureNum = 0
    F_abs = numpy.zeros(shape=(Skeleton_matrix_Normalized.shape[0], int(njoints * (njoints-1)/2)))

    for joints1 in range(njoints-1):
        for joints2 in range(joints1+1,njoints):
            all_X = Skeleton_matrix_Normalized[:, joints1*3] - Skeleton_matrix_Normalized[:, joints2*3]
            all_Y = Skeleton_matrix_Normalized[:, joints1*3+1] - Skeleton_matrix_Normalized[:, joints2*3+1]
            all_Z = Skeleton_matrix_Normalized[:, joints1*3+2] - Skeleton_matrix_Normalized[:, joints2*3+2]

            Abs_distance = numpy.sqrt(all_X**2 + all_Y**2 + all_Z**2)
            F_abs[:, FeatureNum] = Abs_distance
            FeatureNum += 1

    Features = numpy.concatenate((F_pose[2:-2, :], F_velocity[1:-1,:], F_accelerate, F_abs[2:-2, :]), axis = 1)
    return Features

def viterbi_path_log(prior, transmat, observ_likelihood):
    """ Viterbi path decoding
    Wudi first implement the forward pass.
    Future works include forward-backward encoding
    input: prior probability 1*N...
    transmat: N*N
    observ_likelihood: N*T
    """
    T = observ_likelihood.shape[-1]
    N = observ_likelihood.shape[0]

    path = numpy.zeros(T, dtype=numpy.int32)
    global_score = numpy.zeros(shape=(N,T))
    predecessor_state_index = numpy.zeros(shape=(N,T), dtype=numpy.int32)

    t = 1
    global_score[:, 0] =  observ_likelihood[:, 0]
    # print(global_score.shape)
    # need to  normalize the data

    for t in range(1, T):
        for j in range(N):
            # print(global_score[:, t-1].shape)
            # print(prior.shape)

            temp = global_score[:, t-1] + transmat[:, j] + observ_likelihood[j, t]-prior[0]
            global_score[j, t] = max(temp)
            predecessor_state_index[j, t] = temp.argmax()

    path[T-1] = global_score[:, T-1].argmax()

    for t in range(T-2, -1, -1):
        path[t] = predecessor_state_index[ path[t+1], t+1]

    return [path, predecessor_state_index, global_score]

def viterbi_colab_MES(path, global_score, state_no = 5, threshold=-3, mini_frame=15, cls_num = 10):
    """
    Clean the viterbi path output according to its global score,
    because some are out of the vocabulary
    """
    # just to accommodate some frame didn't start right from the begining
    all_label = state_no * cls_num # 20 vocabularies
    start_label = numpy.concatenate((range(0,all_label,state_no), range(1,all_label,state_no),range(2,all_label,state_no)))
    end_label   = numpy.concatenate((range(state_no-3,all_label,state_no), range(state_no-2,all_label,state_no),range(state_no-1,all_label,state_no)))

    begin_frame = []
    end_frame = []
    pred_label = []

    frame = 1
    while(frame < path.shape[-1]-1):
        if path[frame-1]==all_label and path[frame] in start_label:
            begin_frame.append(frame)
            # python integer divsion will do the floor for us :)
            pred_label.append( int(path[frame]/state_no))
            while(frame < path.shape[-1]-1):
                if path[frame] in end_label and path[frame+1]==all_label:
                    end_frame.append(frame)
                    break
                else:
                    frame += 1
        frame += 1

    end_frame = numpy.array(end_frame)
    begin_frame = numpy.array(begin_frame)
    pred_label= numpy.array(pred_label)

    if len(begin_frame)> len(end_frame):
        begin_frame = begin_frame[:-1]
        pred_label = pred_label[:-1]

    elif len(begin_frame)< len(end_frame):# risky hack! just for validation file 668
        end_frame = end_frame[1:]
    ## First delete the predicted gesture less than 15 frames
    frame_length = end_frame - begin_frame
    ## now we delete the gesture outside the vocabulary by choosing
    ## frame number small than mini_frame
    mask = frame_length > mini_frame
    begin_frame = begin_frame[mask]
    end_frame = end_frame[mask]
    pred_label = pred_label[mask]

    Individual_score = []
    for idx, g in enumerate(begin_frame):
            score_start = global_score[path[g], g]
            score_end = global_score[path[end_frame[idx]], end_frame[idx]]
            Individual_score.append(score_end - score_start)

    ## now we delete the gesture outside the vocabulary by choosing
    ## score lower than a threshold
    Individual_score = numpy.array(Individual_score)
    frame_length = end_frame - begin_frame
    # should be length independent
    Individual_score = Individual_score/frame_length

    order = Individual_score.argsort()
    ranks = order.argsort()

    mask = Individual_score > threshold
    begin_frame = begin_frame[mask]
    end_frame = end_frame[mask]
    pred_label = pred_label[mask]
    Individual_score = Individual_score[mask]

    return [pred_label, begin_frame, end_frame, Individual_score, frame_length]

def Extract_feature_Realtime(Pose, njoints):
    #Fcc
    FeatureNum = 0
    Fcc =  numpy.zeros(shape=(Pose.shape[0], int(njoints * (njoints-1)/2*3)))
    for joints1 in range(njoints-1):
        for joints2 in range(joints1+1,njoints):
            Fcc[:, FeatureNum*3:(FeatureNum+1)*3] = Pose[:, joints1*3:(joints1+1)*3]-Pose[:, joints2*3:(joints2+1)*3];
            FeatureNum += 1

    #F_cp
    FeatureNum = 0
    Fcp = numpy.zeros(shape=(Pose.shape[0]-1, njoints **2*3))
    for joints1 in range(njoints):
        for joints2 in range(njoints):
            Fcp[:, FeatureNum*3: (FeatureNum+1)*3] = Pose[1:,joints1*3:(joints1+1)*3]-Pose[0:-1,joints2*3:(joints2+1)*3]
            FeatureNum += 1

    #Instead of initial frame as in the paper Eigenjoints-based action recognition using
    #naive-bayes-nearest-neighbor, we use final frame because it's better initiated
    # F_cf

    Features = numpy.concatenate( (Fcc[0:-1, :], Fcp), axis = 1)
    return Features

def imdisplay(im):
    """ display grayscale images
    """
    im_min = im.min()
    im_max = im.max()
    return (im - im_min) / (im_max -im_min)


from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, render
from main.models.models import Devices, Like

@login_required
def add_like(request, device_id):
    user=request.user
    device = get_object_or_404(Devices, pk=device_id)

    # Sprawdź, czy użytkownik ma już niepolubienie dla tego urządzenia
    existing_dislike = Like.objects.filter(user_id=user, devices_id=device, dislike=True).first()
    if existing_dislike:
        existing_dislike.delete()  # Usuń dislike

    like = Like(user_id=user, devices_id=device, like=True, dislike=False)
    like.save()
    return HttpResponseRedirect(request.META['HTTP_REFERER'])

@login_required
def add_dislike(request, device_id):
    user=request.user
    device = get_object_or_404(Devices, pk=device_id)

    # Sprawdź, czy użytkownik ma już like dla tego urządzenia
    existing_like = Like.objects.filter(user_id=user, devices_id=device, like=True).first()
    if existing_like:
        existing_like.delete()  # Usuń like

    dislike = Like(user_id=user, devices_id=device, like=False, dislike=True)
    dislike.save()
    return HttpResponseRedirect(request.META['HTTP_REFERER'])

@login_required
def remove_like(request, device_id):
    device = get_object_or_404(Devices, pk=device_id)
    user=request.user
    like = Like.objects.filter(user_id=user, devices_id=device, like=True).first()
    if like:
        like.delete()
    return HttpResponseRedirect(request.META['HTTP_REFERER'])

@login_required
def remove_dislike(request, device_id):
    device = get_object_or_404(Devices, pk=device_id)
    user=request.user
    dislike = Like.objects.filter(user_id=user, devices_id=device, dislike=True).first()
    if dislike:
        dislike.delete()
    return HttpResponseRedirect(request.META['HTTP_REFERER'])


"""add geographic name table

Revision ID: 4d1ddc1ec574
Revises: 36d2a94e6894
Create Date: 2020-11-09 13:45:37.277092

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '4d1ddc1ec574'
down_revision = '36d2a94e6894'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('geo_name',
    sa.Column('id', sa.String(length=255), nullable=False),
    sa.Column('name', sa.String(length=255), nullable=False),
    sa.Column('type_', sa.String(length=255), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_geo_name_name'), 'geo_name', ['name'], unique=False)
    op.create_index(op.f('ix_geo_name_type_'), 'geo_name', ['type_'], unique=False)
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index(op.f('ix_geo_name_type_'), table_name='geo_name')
    op.drop_index(op.f('ix_geo_name_name'), table_name='geo_name')
    op.drop_table('geo_name')
    # ### end Alembic commands ###


#!/usr/bin/python
import web
import smbus
import math

urls = (
    '/', 'index'
)

import time

#Hard iron offsets
x_offset = -618.954
y_offset = 733.05

# Power management registers
power_mgmt_1 = 0x6b
power_mgmt_2 = 0x6c

gyro_scale = 131.0
accel_scale = 16384.0

address = 0x68  # This is the address value read via the i2cdetect command

bus = smbus.SMBus(1)  # or bus = smbus.SMBus(1) for Revision 2 boards

now = time.time()

K = 0.85
K1 = 1 - K

time_diff = 0.01

def read_byte(adr):
    return bus.read_byte_data(address, adr)

def read_word(adr):
    high = bus.read_byte_data(address, adr)
    low = bus.read_byte_data(address, adr+1)
    val = (high << 8) + low
    return val

def read_word_2c(adr):
    val = read_word(adr)
    if (val >= 0x8000):
        return -((65535 - val) + 1)
    else:
        return val

def read_all():
    raw_gyro_data = bus.read_i2c_block_data(address, 0x43, 6)
    raw_accel_data = bus.read_i2c_block_data(address, 0x3b, 6)

    gyro_scaled_x = twos_compliment((raw_gyro_data[0] << 8) + raw_gyro_data[1]) / gyro_scale
    gyro_scaled_y = twos_compliment((raw_gyro_data[2] << 8) + raw_gyro_data[3]) / gyro_scale
    gyro_scaled_z = twos_compliment((raw_gyro_data[4] << 8) + raw_gyro_data[5]) / gyro_scale

    accel_scaled_x = twos_compliment((raw_accel_data[0] << 8) + raw_accel_data[1]) / accel_scale
    accel_scaled_y = twos_compliment((raw_accel_data[2] << 8) + raw_accel_data[3]) / accel_scale
    accel_scaled_z = twos_compliment((raw_accel_data[4] << 8) + raw_accel_data[5]) / accel_scale

    return (gyro_scaled_x, gyro_scaled_y, gyro_scaled_z, accel_scaled_x, accel_scaled_y, accel_scaled_z)

def twos_compliment(val):
    if (val >= 0x8000):
        return -((65535 - val) + 1)
    else:
        return val

def dist(a, b):
    return math.sqrt((a * a) + (b * b))

def get_x_rotation(x,y,z):
    radians = math.atan2(y, dist(x,z))
    return math.degrees(radians)

def get_y_rotation(x,y,z):
    radians = math.atan2(x, dist(y,z))
    return -math.degrees(radians)

def get_z_rotation(x,y,z):
    return 0.5

(gyro_scaled_x, gyro_scaled_y, gyro_scaled_z, accel_scaled_x, accel_scaled_y, accel_scaled_z) = read_all()

last_x = get_x_rotation(accel_scaled_x, accel_scaled_y, accel_scaled_z)
last_y = get_y_rotation(accel_scaled_x, accel_scaled_y, accel_scaled_z)
last_z = get_z_rotation(accel_scaled_x, accel_scaled_y, accel_scaled_z)

gyro_offset_x = gyro_scaled_x
gyro_offset_y = gyro_scaled_y
gyro_offset_z = gyro_scaled_z

gyro_total_x = (last_x) - gyro_offset_x
gyro_total_y = (last_y) - gyro_offset_y
gyro_total_z = (last_z) - gyro_offset_z

class index:
    def GET(self):
        global time_diff
        global gyro_scaled_x
        global gyro_scaled_y
        global gyro_scaled_z
        global accel_scaled_x
        global accel_scaled_y
        global accel_scaled_z
        global gyro_x_delta
        global gyro_y_delta
        global gyro_z_delta
        global rotation_x
        global rotation_y
        global rotation_z
        global last_x
        global last_y
        global last_z
        global gyro_total_x
        global gyro_total_y
        global gyro_total_z

        (gyro_scaled_x, gyro_scaled_y, gyro_scaled_z, accel_scaled_x, accel_scaled_y, accel_scaled_z) = read_all()

        gyro_scaled_x -= gyro_offset_x
        gyro_scaled_y -= gyro_offset_y
        gyro_scaled_z -= gyro_offset_z

        gyro_x_delta = (gyro_scaled_x * time_diff)
        gyro_y_delta = (gyro_scaled_y * time_diff)
        gyro_z_delta = (gyro_scaled_z * time_diff)

        gyro_total_x += gyro_x_delta
        gyro_total_y += gyro_y_delta
        gyro_total_z += gyro_z_delta

        rotation_x = get_x_rotation(accel_scaled_x, accel_scaled_y, accel_scaled_z)
        rotation_y = get_y_rotation(accel_scaled_x, accel_scaled_y, accel_scaled_z)

        last_x = K * (last_x + gyro_x_delta) + (K1 * rotation_x)
        last_y = K * (last_y + gyro_y_delta) + (K1 * rotation_y)

        rotation_z = get_z_rotation(last_x, last_y, gyro_total_z)
        last_z = K * (last_z + gyro_z_delta) + (K1 * rotation_z)

        z_angle = (gyro_total_z*6)%360

        #print str(last_x)+" "+str(last_y)+" "+str(z_angle)
        return str(last_x)+" "+str(last_y)+" "+str(get_z_rotation(accel_scaled_x, accel_scaled_y, accel_scaled_z))

if __name__ == "__main__":

    # Now wake the 6050 up as it starts in sleep mode
    bus.write_byte_data(address, power_mgmt_1, 0)

    app = web.application(urls, globals())
    app.run()