import re
import string
from nltk.corpus import stopwords
import random
import unittest
import os

"""Test cases for my code, use: python -m unittest test_file_name.py in terminal"""

class ClosedHashtable:

    def __init__(self):
        self.hashsize = 100
        self.table = [None]*self.hashsize
        self.stop_words = set(stopwords.words('english'))
        self.words = []

        # max of 50 words being searched for in the part 2 section due to k <= 50, hence hashtablesize of 50
        # Yes, it doesnt include all the words within the textfile, but it only includes the exact amount words necessary

        self.p2_hashsize = 50
        self.p2_table = [None]*self.p2_hashsize

    def fileloader(self, filepath):
        self.root = None
        words = self.parse_file(filepath)
        for word in words:
            self.words.append(word)

    def parse_file(self, filepath):
        with open(filepath, 'r') as f:
            textfile = f.readlines()
            words = []
            for line in textfile:
                # textfile is now a string of the text file
                line = line.strip().lower()
                # seperated each line via periods
                line = line.translate(
                    str.maketrans('', '', string.punctuation))
                # eliminated periods
                line = self.remove_stopwords(line)
                # got rid of redudent terms such as the, this, is ETC

                line = re.sub('\d+', '', line)
                line = re.sub(' +', ' ', line)
                # now put into list form
                if line:
                    words.extend(line.split(" "))
            return words

    def remove_stopwords(self, text):
        return " ".join([word for word in str(text).split() if word not in self.stop_words])

    def hashfunc(self, word):
        total = 0

        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.hashsize
        return total

    def add(self, words):
        for word in words:
            slot = self.hashfunc(word)
            start = slot
            insert = False
            while insert == False:

                if self.table[slot] is None:
                    self.table[slot] = [word, 1]
                    insert = True
                    break
                elif word == self.table[slot][0]:
                    self.table[slot][1] += 1
                    insert = True
                    break
                slot = (slot+1) % self.hashsize
                if slot == start:
                    insert = True
                    break
        print("CLOSED HASH TABLE p1 of size: ", self.hashsize)
        print("")
        print(self.table)
        print("")
        "---part2------------------------------------------------------"

    def p2_hashfunc(self, word):
        total = 0

        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.p2_hashsize
        return total

    def p2_add(self, words):
        for word in words:
            slot = self.p2_hashfunc(word)
            start = slot
            insert = False
            while insert == False:
                if self.p2_table[slot] is None:
                    self.p2_table[slot] = [word, 1]
                    insert = True
                    break
                elif word == self.p2_table[slot][0]:
                    self.p2_table[slot][1] += 1
                    insert = True
                    break
                slot = (slot+1) % self.p2_hashsize
                if slot == start:
                    insert = True
                    break
        print("---------------CLOSED HASH TABLE p2 of size: ", self.p2_hashsize)
        print("")
        print(self.p2_table)

    def p2_searchtest(self):
        # searching function for closded hash table
        # starts at step = 1, because im counting going to the hashing index as a step
        for k in range(5, 30, 5):
            print("")
            print("k =", k*2)
            print("")
            # list of words we will look for
            list = []
            # add k*2 words to list since in my loop k is half of the actual k
            for i in range(k):
                # allows me to add one word in the table and one word not in the table
                x = random.randint(0, len(self.p2_table)-1)
                list.append(self.p2_table[x][0])
                x = "notinlist"+str(i)
                list.append(x)
            # iterate through words in our list to find
            for word in list:
                slot = self.p2_hashfunc(word)
                print("searching for: ", word, ", starting at index: ", slot)
                start = slot
                found = False
                steps = 1
                while found == False:
                    if word == self.p2_table[slot][0]:
                        found = True
                        print(word, "is in the table, after", steps, "steps")
                        print("")
                        break
                    slot = (slot+1) % self.p2_hashsize
                    steps += 1
                    # if done full loop and not found, then not in table
                    if slot == start:
                        found = True
                        print(word, "is not in the table -1")
                        print("")
                        break

obje = ClosedHashtable()
obje.fileloader('D:/A3test.txt')
obje.add(obje.words)
obje.p2_add(obje.words)
obje.p2_searchtest()

"-----OPEN HASH TABLE----------------"

class Node:

    def __init__(self, data, freq):
        self.data = data
        self.next = None
        self.freq = freq

class OpenHashtable:

    def __init__(self,):
        # NOTE: while coding i changed some things to cater towards pt2, hence the seperate functions for p1 and p2
        # but im realizing now alot of it wasnt necessary, regardless it still follows the instructions correctly
        # just harder to read
        self.hashsize = 13
        self.hashtable = []
        self.stop_words = set(stopwords.words('english'))
        self.words = []

        # max amount of values being stored is 50, so having around 4 per bucket on avg seems optimum to avoid any values having their own bucket
        self.p2_hashsize = 13
        self.p2_hashtable = []
        self.p2_words = []

    def fileloader(self, filepath):
        self.root = None
        words = self.parse_file(filepath)
        for word in words:
            self.words.append(word)

    def parse_file(self, filepath):
        with open(filepath, 'r') as f:
            textfile = f.readlines()
            words = []
            for line in textfile:
                # textfile is now a string of the text file
                line = line.strip().lower()
                # seperated each line via periods
                line = line.translate(
                    str.maketrans('', '', string.punctuation))
                # eliminated periods
                line = self.remove_stopwords(line)
                # got rid of redudent terms such as the, this, is ETC

                line = re.sub('\d+', '', line)
                line = re.sub(' +', ' ', line)
                # now put into list form
                if line:
                    words.extend(line.split(" "))
            return words

    def remove_stopwords(self, text):
        return " ".join([word for word in str(text).split() if word not in self.stop_words])

    def hashfunc(self, word):
        # using same hash function through entirety of code as introduced by the slides in class
        total = 0
        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.hashsize

        return total

    def construct(self):
        for i in range(self.hashsize):
            add = Node('Head', 0)
            self.hashtable.append([add])
            # print(self.hashtable[i][0].data)

    # adds to hash table
    def add(self):
        for word in self.words:
            new_node = Node(word, 1)
            # get correct slot
            slot = self.hashfunc(word)
            # set current to the first node in the slot
            curr = self.hashtable[slot][0]
            done = False
            # if the first node is empty, add the new node
            while done == False:
                if curr.next == None and curr.data != new_node.data:
                    curr.next = new_node
                    done = True
                # add to word frequency if the word is already in the hashtable
                elif curr.data == new_node.data:
                    curr.freq += 1
                    done = True
                # traverse
                curr = curr.next

    # prints the hashtable

    def print(self):
        print("--------------OPEN HASHTABLE Using LinkedList Part 1-------")
        for i in range(self.hashsize):
            print("index", i)
            # set current to the first node in the slot
            curr = self.hashtable[i][0]
            # traverse
            list = []
            while curr != None:
                x = curr.data, curr.freq
                list.append(x)
                # print(curr.data, curr.freq)
                curr = curr.next
            print(list)

    """ -------------------- PART 2 for open ------------------------------------------------------------"""

    def construct2(self):
        for i in range(self.p2_hashsize):
            add = Node('Head', 0)
            self.p2_hashtable.append([add])

    def add2(self):

        # gets 50 random words from the list of all the words in the text file
        for i in range(50):
            x = random.randint(0, len(self.words)-1)
            self.p2_words.append(self.words[x])
        # iterates through the chosen words and adds them to the hashtable
        for word in self.p2_words:
            new_node = Node(word, 1)
            # get correct slot
            slot = self.hashfunc2(word)
            # set current to the first node in the slot
            curr = self.p2_hashtable[slot][0]
            done = False
            # if the first node is empty, add the new node
            while done == False:
                if curr.next == None and curr.data != new_node.data:
                    curr.next = new_node
                    done = True
                # add to word frequency if the word is already in the hashtable
                elif curr.data == new_node.data:
                    curr.freq += 1
                    done = True
                # traverse
                curr = curr.next

    # conducts search for pt 2 w/ helper function
    def search2(self):

        for k in range(5, 25, 5):
            print(" going to index counts as a step, lowest steps possible as per my definition is 3 due to head node")
            print("k =", k*2)
            print("")
            list = []
            # adds words to list following assignment instructions
            for i in range(k):
                x = random.choice(self.p2_words)
                list.append(x)
                x = "notinlist"
                list.append(x)
                # iterates through each word we are looking for and sends it to be searched
            for i in list:
                self.searchhelper2(i)

    # takes word from search list as input and searches for it in the hashtable
    def searchhelper2(self, word):
        # start steps at 1 because im counting going to correct bucket as a step
        # an item cannot have less then 3 steps due to first step being going to correct bucket, and first item in bucket being head placeholder
        # therefore requiring 3 steps to get to the first possible node of any of the buckets
        steps = 1
        found = False
        slot = self.hashfunc2(word)
        curr = self.p2_hashtable[slot][0]
        while curr != None:
            if curr.data == word:
                steps += 1
                print("found", word, "in", steps, "steps")
                print("")
                found = True
                break
            else:
                steps += 1
                curr = curr.next
                if curr == None:
                    print("not found", word, "-1")
                    print("")
                    break

    def hashfunc2(self, word):
        total = 0

        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.p2_hashsize

        return total

    # prints out the hashtable
    def print2(self):
        print("")
        print("----------OPEN Hashtable for part 2------")
        print(" going to index counts as a step, lowest steps possible as per my definition is 3 due to head node")
        for i in range(self.p2_hashsize):
            print("")
            print("index", i)
            print("")
            # set current to the first node in the slot
            curr = self.p2_hashtable[i][0]
            # traverse
            list = []
            while curr != None:
                x = curr.data, curr.freq
                list.append(x)

                curr = curr.next
            print(list)

    def main(self):
        obj.fileloader('D:/A3test.txt')
        obj.construct()
        obj.add()
        obj.print()

    def main2(self):
        obj2.fileloader('D:/A3test.txt')
        obj2.construct2()
        obj2.add2()
        obj2.print2()
        obj2.search2()

obj = OpenHashtable()
obj.main()

obj2 = OpenHashtable()
obj2.main2()


#!/usr/bin/python

import os,sys
def perm_check():
    USERID = os.getuid()
    if USERID != 0:
        print('[!] run this script as root.')
        sys.exit(1)

perm_check()

import logging
import argparse, subprocess,multiprocessing
from scapy.all import *

def run_core(pdst):
    try:
        answer=sr1(ARP(pdst=pdst),timeout=2, verbose=False)
    except Exception as err:
        print(err)
        sys.exit(1)
    else:
        if answer == None:
            pass
        else:
            print(f'{pdst} -> {answer.hwsrc}')

parser = argparse.ArgumentParser(description='SCAPY ARP Host Discovery')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-i', '--interface',
        metavar='<INTERFACE>',
        dest='interface',
        help='Interface to discover hosts.')
group.add_argument('-p', '--ip',
        metavar='<TARGET IP>',
        dest='target_ip',
        help='Target this single host only.')
group.add_argument('-l', '--ip-list',
        metavar='<IP LIST>',
        dest='ip_list',
        help='file containing ips separated by newline')
args = parser.parse_args()

if args.target_ip:
    run_core(pdst=args.target_ip)

def interface_check(interface):
    rc = subprocess.run(['ip', 'addr', 'show', interface], stdout=open('/dev/null'), stderr=open('/dev/null'))
    if rc.returncode != 0:
        print(f'[!] interface[{interface}] seems invalid. please double check.')
        sys.exit(1)

if args.interface:
    interface = str(args.interface)
    interface_check(interface)

    ip = subprocess.check_output(f"ip addr show {interface} | grep inet | grep -v inet6 | sed 's/  //g' | cut -d ' ' -f 2", shell=True).decode().strip().split('/')[0]
    prefix = ip.split('.')[0] + '.' + ip.split('.')[1] + '.' + ip.split('.')[2] + '.'
    processes = []
    for addr in range(1,254):
        pdst = prefix + str(addr)
        p = multiprocessing.Process(target=run_core, args=[pdst])
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

def file_check(file):
    try:
        open(file)
    except Exception as err:
        print(err)
        sys.exit(1)

if args.ip_list:
    ip_list = str(args.ip_list)
    file_check(ip_list)
    processes = []
    with open(ip_list) as ips:
        for line in ips:
            pdst = line.strip()
            p = multiprocessing.Process(target=run_core, args=[pdst])
            processes.append(p)
            p.start()

        for p in processes:
            p.join()


from flask import Flask, request, render_template, url_for
from main import make_userfile
from classes import User, WordController

ACC_NAME = ''
FILE_NAME = ''
USER = User()
TRUE_ANSWER = ''

app = Flask(__name__)

@app.route('/')
def my_form():
    '''
    Returns start page.
    '''
    return render_template('start_page.html')

@app.route('/log_in', methods=['POST'])
def log_in():
    '''
    Returns page that asks if it's your first time
    using this program if you enter right account name
    if you enter wrong account name - returns start page
    '''
    if request.method == 'POST':
        acct = request.form['accaunt']
        if " " not in acct and acct != "":
            global ACC_NAME
            ACC_NAME = acct
            return render_template('time_usage_page.html')
        else:
            return render_template('start_page2.html')

@app.route('/log_in2', methods=['POST'])
def log_in2():
    '''
    Returns page with options - menu page
    '''
    if request.method == 'POST':
        usg_time = request.form['usage_time']
        answ = ['yes', 'YES', 'Yes', 'no', 'NO', 'No']
        if usg_time in answ:
            file_name = make_userfile(ACC_NAME, usg_time)
            global FILE_NAME
            FILE_NAME = file_name
            global USER
            USER = User(FILE_NAME)
            USER.set_file(FILE_NAME)
            return render_template('options.html')
        else:
            return render_template('time_usage_page2.html')

@app.route('/learn', methods=['POST'])
def learn():
    '''
    Return page for learning new word
    '''
    if request.method == 'POST':
        word = USER.learn_new_word()
        if word == None:
            return render_template('no_more_words.html')
        word = word.split("\n")
    return render_template('learn_word.html', word=word)

@app.route('/test', methods=['POST'])
def test():
    '''
    Returns test page
    '''
    if request.method == 'POST':
        answers = USER.test_yourself(1)
        if answers == False:
            return render_template('no_words.html')
        else:
            global TRUE_ANSWER
            TRUE_ANSWER = answers[0]
            false_answ = answers[1]
            word = answers[2]
            return render_template('test_yourself.html', answers = false_answ, word = word)

@app.route('/check_test', methods=['POST'])
def check_test():
    '''
    Check test and returns True of False page depending on user answer
    '''
    if request.method == 'POST':
        client_answ = request.form['client_answ']
        if client_answ == TRUE_ANSWER:
            return render_template("true_an.html", true_answ = TRUE_ANSWER)
        else:
            return render_template("false_an.html",  true_answ = TRUE_ANSWER)

@app.route('/wordlist', methods=['POST'])
def wordlist():
    '''
    Returns page with s wordlist
    '''
    if request.method == 'POST':
        words = USER.see_word_list()
        words = words.split("\n")
    return render_template('see_wordlist.html', words = words)

@app.route('/log_out', methods=['POST'])
def log_out():
    '''
    Returns start page
    '''
    return render_template('start_page.html')

@app.route('/options', methods=['POST'])
def back_to_options():
    '''
    Returns options-menu page
    '''
    return render_template('options.html')

if __name__ == "__main__":
    app.run()


__author__ = 'Chris'
import serial
from time import sleep
import logging
import math
#######
# Object handles requesting and parsing weather info from an Arduino
# Init with serial port (IE: /dev/ttyACM0), baud rate, and timeout
#

class Arduino(object):

    def __init__(self, port, speed, timeout):
        self.port = port
        self.speed = speed
        self.timeout = timeout
        self.serial = serial.Serial()
        self.serial.port = self.port
        self.serial.baudrate = self.speed
        self.serial.timeout = self.timeout
        self.logger = logging.getLogger(__name__)
        self.logger.info("Arduino Instantiated.")

        # Nodes
        self.node1 = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.node2 = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.node3 = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.lacrosse = {'stationid': '', 'temp': '', 'rh': '', 'windspeed': '', 'winddir': 0, 'rainfall': 0,
                         'dewpoint': 0, 'humidex': 0}

        # store previous value
        self.node1last = dict(self.node1)
        self.node2last = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.node3last = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.nodelast = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}

    def validate(self, node):
        # allow 30% difference between readings
        allowedVariance = .3
        low = 1 - allowedVariance
        high = 1 + allowedVariance

        # minimum and maximum values - anything outside these values is an error
        vMin = 0
        vMax = 1000
        tempMin = -50
        tempMax = 50
        ageMax = 700

        # check for allowed min/max range
        self.logger.debug("Checking min/maxes (temp/vin/vbat/age): "+str(node['temp']) +
                          "/"+str(node['vin'])+"/"+str(node['vbat'])+"/"+str(node['age']))
        if (node['temp'] < tempMin) or (node['temp'] > tempMax):
            self.logger.debug("Temp out of range")
            return False

        if (node['vin'] < vMin or node['vin'] > vMax or node['vbat'] < vMin or node['vbat'] > vMax):
            self.logger.debug("vBat or vIn out of range")
            return False

        if node['age'] > ageMax:
            self.logger.debug("Age out of range")
            return False

        # check for allowed variances
        self.logger.debug("validating address: " + str(node['address']))
        address = node['address']
        if (address == 1):
            self.logger.debug("Node1last")
            nodelast = dict(self.node1last)
        elif (address == 2):
            self.logger.debug("Node2last")
            nodelast = dict(self.node2last)
        elif (address == 3):
            self.logger.debug("Node3last")
            nodelast = dict(self.node3last)
        else:
            return False

        # on first run, nodelast will be empty - we will assume that it's valid.
        self.logger.debug("Nodelast: " + str(nodelast['temp']))
        if (nodelast['address'] == ''):
            self.logger.debug("First run, return true")
            if (address == 1):
                self.node1last = dict(node)
            elif (address == 2):
                self.node2last = dict(node)
            elif (address == 3):
                self.node3last = dict(node)
            return True

        oldTemp = nodelast['temp']
        oldVcc = nodelast['vin']
        oldVbat = nodelast['vbat']
        newTemp = node['temp']
        newVcc = node['vin']
        newVbat = node['vbat']

        self.logger.debug("Checking allowed variance range of: " + str(oldTemp*high)+"<"+str(newTemp)+"<"+str(oldTemp*low))
        if (newTemp < oldTemp*low or newTemp > oldTemp*high):
            self.logger.debug("Temp failure: " + str(oldTemp*high)+"<"+str(newTemp)+"<"+str(oldTemp*low))
            return False

        if (newVbat < oldVbat*low or newVbat > oldVbat*high):
            self.logger.debug("vbat failure: " + str(oldVbat*high)+"<"+str(newVbat)+"<"+str(oldVbat*low))
            return False

        if (newVcc < oldVcc*low or newVcc > oldVcc*high):
            self.logger.debug("vcc failure: " + str(oldVcc*high)+"<"+str(newVcc)+"<"+str(oldVcc*low))
            return False

        self.logger.debug("Variance ranges passed")
        # all conditions passed - update nodelast and return true
        if (address == 1):
            self.node1last = dict(node)
        elif (address == 2):
            self.node2last = dict(node)
        elif (address == 3):
            self.node3last = dict(node)

        self.logger.debug("All tests passed!")
        return True

    def open(self):
        if not self.serial.isOpen():
            self.logger.debug("Opening serial connection")
            self.serial.open()

            # when opening a connection, arduino returns garbage first
            # flush it, and ignore the output
            self.serial.write(b'|')
            self.serial.readline()
            self.serial.flushInput()
            self.logger.debug("Opened")

    def isOpen(self):
        return self.serial.isOpen()

    def close(self):
        if self.serial.isOpen():
            self.serial.close()

    def writeSerial(self, value):
        # make sure port is open
        if not self.isOpen():
            self.logger.debug("Port closed")
            self.open()

        #flush buffer
        self.serial.write(b'|')
        self.serial.readline()

        sleep(0.1)
        self.serial.flushInput()

        self.logger.debug("Writing value: " + value)
        self.serial.write(bytearray(value,'utf-8'))
        self.serial.write(b'|')

        result = self.serial.readline()
        self.logger.debug("Result: \n\t" + result)
        self.close()
        return result

    def calcDewPoint(self, RH, temp):
        dewpoint = math.pow((RH/100.0), 0.125)
        self.logger.debug("Dewpoint step 1: " + str(dewpoint))
        dewpoint = dewpoint * (112+0.9*temp) + (0.1 * temp) - 112
        self.logger.debug("Dewpoint step 2: " + str(dewpoint))
        return round(dewpoint)

    def calcHumidex(self, dewpoint, temp):
        dewK = dewpoint + 273.15
        exponent = 5417.7530 * (1/273.16 - 1/dewK)
        humidex = temp + 0.5555 * (6.11 * math.pow(math.e, exponent) - 10)
        return round(humidex,1)

    def readValue(self, node):
        if (node == "node1"):
            result = self.writeSerial("node1")

            self.logger.info("Node 1 Received.")

            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.node1['address'] = int(resultList[0])
                self.node1['uptime'] = int(resultList[1])
                if resultList[2] == "ovf":
                        # temp probe error
                    self.node1['temp'] = -100
                else:
                    self.node1['temp'] = float(resultList[2])
                self.node1['vin'] = int(resultList[3])
                self.node1['vbat'] = int(resultList[4])
                self.node1['age'] = int(resultList[5])

                if self.validate(self.node1) is False:
                    self.logger.error("Node 1 out of range - data old or invalid")
                    self.logger.error("Value:\n" + str(self.node1))
                    self.node1['error'] = True
                else:
                    self.node1["error"] = False
                self.logger.debug("Node1: \n" + str(self.node1) + "\n\n")
            except:
                self.node1["error"] = True
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("node1")

        elif (node == "node2"):
            result = self.writeSerial("node2")

            self.logger.info("Node 2 Received.")

            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.node2['address'] = int(resultList[0])
                self.node2['uptime'] = int(resultList[1])
                if resultList[2] == "ovf":
                    # temp probe error
                    self.node2['temp'] = -100
                else:
                    self.node2['temp'] = float(resultList[2])
                self.node2['vin'] = int(resultList[3])
                self.node2['vbat'] = int(resultList[4])/2
                self.node2['age'] = int(resultList[5])

                if self.validate(self.node2) is False:
                    self.logger.error("Node 2 out of range - data old or invalid")
                    self.logger.error("Value:\n" + str(self.node2))
                    self.node2['error'] = True
                else:
                    self.node2["error"] = False
                self.logger.debug("node2: \n" + str(self.node2) + "\n\n")
            except:
                self.node2["error"] = True
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("node2")

        elif (node == "node3"):
            result = self.writeSerial("node3")

            self.logger.info("Node 3 Received.")

            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.node3['address'] = int(resultList[0])
                self.node3['uptime'] = int(resultList[1])
                if resultList[2] == "ovf":
                    # temp probe error
                    self.node3['temp'] = -100
                else:
                    self.node3['temp'] = float(resultList[2])
                self.node3['vin'] = int(resultList[3])
                self.node3['vbat'] = int(resultList[4])
                self.node3['age'] = int(resultList[5])

                if self.validate(self.node3) is False:
                    self.logger.error("Node 3 out of range - data old or invalid")
                    self.logger.error("Value:\n" + str(self.node3))
                    self.node3['error'] = True
                else:
                    self.node3["error"] = False
                self.logger.debug("node3: \n" + str(self.node3) + "\n\n")
            except:
                self.node3["error"] = True
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("node3")

        elif (node == "lacrosse"):
            result = self.writeSerial("lacrosse")

            self.logger.info("LaCrosse Received.")
            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.lacrosse["stationid"] = int(resultList[0])
                self.lacrosse["temp"] = float(resultList[1])/10.0
                self.lacrosse["rh"] = int(resultList[2])
                self.lacrosse["windspeed"] = float(resultList[3])/10.0
                self.lacrosse["winddir"] = int(resultList[4])
                self.lacrosse["rainfall"] = int(resultList[5])
                self.logger.debug("calculating dewpoint")
                self.lacrosse["dewpoint"] = self.calcDewPoint(self.lacrosse["rh"], self.lacrosse["temp"])
                self.logger.debug("calculating humidex")

                # only calculate humidex is RH is above 30%
                self.lacrosse["humidex"] = self.calcHumidex(self.lacrosse["dewpoint"], self.lacrosse["temp"])
                self.logger.debug("lacrosse: \n" + str(self.lacrosse) + "\n\n")
            except:
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("lacrosse")

    def updateAll(self):
        self.logger.debug("Updating all nodes")

        self.readValue("node1")
        sleep(0.1)
        self.readValue("node2")
        sleep(0.1)
        self.readValue("node3")
        sleep(0.1)
        self.readValue("lacrosse")

    def printCurrent(self):
        self.writeSerial('|')
        print(self.writeSerial('current'))
        print(self.serial.readlines())

class Pressure(object):

    def __init__(self, port, speed, timeout):
        self.port = port
        self.speed = speed
        self.timeout = timeout
        self.serial = serial.Serial()
        self.serial.port = self.port
        self.serial.baudrate = self.speed
        self.serial.timeout = self.timeout
        self.logger = logging.getLogger(__name__)
        self.logger.info("Pressure Instantiated.")

        self.pressure = ''

    def open(self):
        if not self.serial.isOpen():
            self.logger.debug("Opening serial connection")
            self.serial.open()
            self.logger.debug("Opened")

    def close(self):
        if self.serial.isOpen():
            self.serial.close()

    def writeSerial(self, value):
        # make sure port is open
        if not self.serial.isOpen():
            self.logger.debug("Port closed")
            self.open()

        self.serial.flushInput()

        self.logger.debug("Writing value: " + value)
        self.serial.write(bytearray(value, 'utf-8'))

        sleep(0.1)
        self.serial.readline()

        sleep(0.1)
        result = self.serial.readline()
        self.logger.debug("Result: \n\t" + result)
        self.close()
        return result

    def getPressure(self):
        self.pressure = float(self.writeSerial("pres"))
        return self.pressure


from engine_factory import EngineFactory
from db.cursor_factory import ConnectionFactory
from model import KnowledgeTableColumnMapRecord, KnowledgeTableRowMapRecord, APIHTMLText
from model_factory import KnowledgeTableFactory
from shared.logger_util import Logger

class APIHTMLTextImport:
    """
    import the API HTML from one column of one table
    """

    def __init__(self, table, primary_key_name, html_column, html_text_type, session=None, commit_step=5000):
        self.table = table
        self.primary_key_name = primary_key_name
        self.html_column = html_column
        self.html_text_type = html_text_type
        self.commit_step = commit_step
        self.logger_file_name = "import_html_for_" + self.table
        self.logger = None
        self.session = session
        self.api_html_table = None
        self.api_knowledge_table = None
        self.data_source_knowledge_table = None

    def start_import(self):
        self.logger = Logger(self.logger_file_name).get_log()
        if not self.session:
            self.session = EngineFactory.create_session()
        self.init_knowledge_table()

        cur = ConnectionFactory.create_cursor_by_knowledge_table(self.data_source_knowledge_table)

        select_sql = "select {primary_key_name},{html_column} from {table}".format(
            primary_key_name=self.primary_key_name,
            html_column=self.html_column, table=self.table)
        cur.execute(select_sql)
        data_list = cur.fetchall()
        result_tuples = []
        for i in range(0, len(data_list)):
            row_data = data_list[i]
            primary_key = row_data[0]
            html_text = row_data[1]

            if KnowledgeTableColumnMapRecord.exist_import_record(session=self.session,
                                                                 start_knowledge_table=self.data_source_knowledge_table,
                                                                 end_knowledge_table=self.api_html_table,
                                                                 start_row_id=primary_key,
                                                                 start_row_name=self.html_column):
                self.logger.info("%d has been import to new table", primary_key)
                continue
            api_html_text = self.create_from_one_row_data(primary_key, html_text)

            if api_html_text:
                api_html_text = api_html_text.create(self.session, autocommit=False)
                result_tuples.append((api_html_text, primary_key))
            else:
                self.logger.warn("None api_html_text fot %s", str(row_data))
                continue

            if len(result_tuples) > self.commit_step:
                self.commit_to_server_for_column_map(map_tuples=result_tuples)
                result_tuples = []
        self.commit_to_server_for_column_map(map_tuples=result_tuples)
        self.logger.info("import api html completed!")
        cur.close()

    def init_knowledge_table(self):
        self.api_html_table = KnowledgeTableFactory.get_api_html_table(self.session)
        self.api_knowledge_table = KnowledgeTableFactory.get_api_entity_table(self.session)
        self.data_source_knowledge_table = KnowledgeTableFactory.find_knowledge_table_by_name(self.session, self.table)

    def create_from_one_row_data(self, primary_key, html_text):

        new_primary_id_for_api = self.get_api_new_primary_key_by_old_primary_key(old_primary_id=primary_key)

        api_html_text = self.create_api_html_text_html_entity(api_id=new_primary_id_for_api, html_text=html_text)

        return api_html_text

    def get_api_new_primary_key_by_old_primary_key(self, old_primary_id):
        new_primary_id_for_api = KnowledgeTableRowMapRecord.get_end_row_id(session=self.session,
                                                                           start_knowledge_table=self.data_source_knowledge_table,
                                                                           end_knowledge_table=self.api_knowledge_table,
                                                                           start_row_id=old_primary_id)

        if new_primary_id_for_api is None:
            self.logger.error("no new_primary_id_for_api for old_primary_id=%d", old_primary_id)
            return None
        else:
            self.logger.info("old_primary_id=%d -> new_primary_id_for_api =%d", old_primary_id, new_primary_id_for_api)
            return new_primary_id_for_api

    def create_api_html_text_html_entity(self, api_id, html_text):
        if not html_text or not html_text.strip() or html_text=="null":
            self.logger.error("no html_text")
            return None

        if not api_id:
            self.logger.error("no api_id ")
            return None
        api_html_entity = APIHTMLText(api_id=api_id, html=html_text, html_type=self.html_text_type)

        return api_html_entity

    def commit_to_server_for_column_map(self, map_tuples):
        self.logger.info("success=%d", len(map_tuples))
        try:
            self.session.commit()

            for (relation, old_id) in map_tuples:
                record = KnowledgeTableColumnMapRecord(self.data_source_knowledge_table, self.api_html_table, old_id,
                                                       relation.id, self.html_column)
                record.create(self.session, autocommit=False)
            self.session.commit()
        except Exception:
            self.logger.exception("commit_to_server_for_column_map")


import tqdm
import numpy as np
import csv
import six
from collections import OrderedDict
from collections import Iterable

import keras
from keras.callbacks import ModelCheckpoint
from keras.callbacks import TensorBoard
from keras.callbacks import CSVLogger
from keras.callbacks import LearningRateScheduler

class Evaluate(keras.callbacks.Callback):
    def __init__(
        self,
        generator,
        eval_steps,
        is_patch,
        verbose=1):

        super(Evaluate, self).__init__()
        self.generator = generator
        self.eval_steps = eval_steps
        self.is_patch = is_patch
        self.verbose = verbose

        self.tot_dice = 0.
        self.pneu_dice = 0.

    def on_epoch_end(self, epoch, logs=None):
        if epoch % 5 == 0:
            logs = logs or {}
            self.tot_dice, self.pneu_dice = self.evaluate()

            if self.verbose == 1:
                print('Epoch {:4d} - Dice score : {:.4f} | Pneu score : {:.4f}'.format(epoch+1, self.tot_dice, self.pneu_dice))

        logs['eval_dice'] = self.tot_dice
        logs['eval_dice_pneu'] = self.pneu_dice

    def dice(self, y_true, y_pred, classes, smooth=1.):
        loss = 0.
        y_pred = np.clip(y_pred, 1e-7, 1-1e-7)
        if classes > 1:
            for num_label in range(classes):
                y_true_f = y_true[...,num_label].flatten()
                y_pred_f = y_pred[...,num_label].flatten()
                intersection = np.sum(y_true_f * y_pred_f)
                loss += (2. * intersection + smooth) / (np.sum(y_true_f) + np.sum(y_pred_f) + smooth)

        else:
            y_true_f = y_true.flatten()
            y_pred_f = y_pred.flatten()
            intersection = np.sum(y_true_f * y_pred_f)
            loss += (2. * intersection + smooth) / (np.sum(y_true_f) + np.sum(y_pred_f) + smooth)

        return loss / classes

    def onehot(self, x, classes):
        result = np.zeros(x.shape+(classes,))
        for i in range(classes):
            result[...,i][np.where(x == i)] = 1.
        return result

    def evaluate(self, classes=2):
        tot_dice = []
        pneu_dice = []
        for i in tqdm.trange(self.eval_steps):
            y_true = np.zeros((1024, 1024, 2))
            y_pred = np.zeros((1024, 1024, 2))
            loc_cnt = np.zeros((1024, 1024, 2))

            g = next(self.generator)
            result = self.model.predict_on_batch(g[0])

            if self.is_patch:
                ratio = 1024//result.shape[1]
                for l in range(result.shape[0]):
                    # result = model.predict_on_batch(g[0][l][np.newaxis,...])
                    y_true[(l//(2*ratio-1))*1024//(ratio*2):(l//(2*ratio-1)+2)*1024//(ratio*2),
                           (l%(2*ratio-1))*1024//(ratio*2):(l%(2*ratio-1)+2)*1024//(ratio*2)] += g[1][l]

                    y_pred[(l//(2*ratio-1))*1024//(ratio*2):(l//(2*ratio-1)+2)*1024//(ratio*2),
                           (l%(2*ratio-1))*1024//(ratio*2):(l%(2*ratio-1)+2)*1024//(ratio*2)] += result[l]

                    loc_cnt[(l//(2*ratio-1))*1024//(ratio*2):(l//(2*ratio-1)+2)*1024//(ratio*2),
                            (l%(2*ratio-1))*1024//(ratio*2):(l%(2*ratio-1)+2)*1024//(ratio*2)] += 1

                y_true /= loc_cnt
                y_pred /= loc_cnt

            else:
                y_pred = result

            y_pred = self.onehot(np.argmax(y_pred, axis=-1), classes)
            tot_dice.append(self.dice(y_true, y_pred, classes))
            pneu_dice.append(self.dice(y_true[...,1], y_pred[...,1], 1))

        return sum(tot_dice)/eval_steps, sum(pneu_dice)/eval_steps

class CustomBatchLogger(CSVLogger):
    def __init__(self, filename, separator=',', append=False):
        super().__init__(filename, separator, append)

    def on_train_begin(self, logs=None):
        super().on_train_begin(logs)

    def on_batch_end(self, batch, logs=None):
        logs = logs or {}

        def handle_value(k):
            is_zero_dim_ndarray = isinstance(k, np.ndarray) and k.ndim == 0
            if isinstance(k, six.string_types):
                return k
            elif isinstance(k, Iterable) and not is_zero_dim_ndarray:
                return '"[%s]"' % (', '.join(map(str, k)))
            else:
                return k

        if self.keys is None:
            self.keys = sorted(logs.keys())

        if self.model.stop_training:
            # We set NA so that csv parsers do not fail for this last batch.
            logs = dict([(k, logs[k] if k in logs else 'NA') for k in self.keys])

        if not self.writer:
            class CustomDialect(csv.excel):
                delimiter = self.sep
            fieldnames = ['batch'] + self.keys
            if six.PY2:
                fieldnames = [unicode(x) for x in fieldnames]
            self.writer = csv.DictWriter(self.csv_file,
                                         fieldnames=fieldnames,
                                         dialect=CustomDialect)
            if self.append_header:
                self.writer.writeheader()

        row_dict = OrderedDict({'batch': batch})
        row_dict.update((key, handle_value(logs[key])) for key in self.keys)
        self.writer.writerow(row_dict)
        self.csv_file.flush()

    def on_epoch_end(self, epoch, logs=None):
        pass

    def on_train_end(self, logs=None):
        super().on_train_end(logs)

def callback_checkpoint(filepath, monitor, verbose, mode, save_best_only, save_weights_only):
    return ModelCheckpoint(filepath=filepath,
                           monitor=monitor,
                           verbose=verbose,
                           mode=mode,
                           save_best_only=save_best_only,
                           save_weights_only=save_weights_only)

def callback_epochlogger(filename, separator, append):
    return CSVLogger(filename=filename,
                     separator=separator,
                     append=append)

def callback_batchlogger(filename, separator, append):
    return CustomBatchLogger(filename=filename,
                             separator=separator,
                             append=append)

def callback_tensorboard(log_dir, batch_size):
    return TensorBoard(log_dir=log_dir, batch_size=batch_size)

def main_schedule(initlr, mode, value, duration, total_epoch):
    def _cosine_anneal_schedule(epoch, total_epoch):
        total_epoch = np.maximum(total_epoch, 50)
        cos_inner = np.pi * (epoch % total_epoch)
        cos_inner /= total_epoch
        cos_out = np.cos(cos_inner) + 1
        return float(initlr / 2 * cos_out)

    def _exponential(epoch, value, duration):
        update = epoch // duration
        return initlr * (value ** update)

    def _schedule(epoch, lr):
        if mode == 'exponential':
            return _exponential(epoch, value, duration)
        elif mode == 'cosine':
            return _cosine_anneal_schedule(epoch, total_epoch)

    return _schedule

def callback_learningrate(initlr, mode, value, duration, total_epoch):
    return LearningRateScheduler(schedule=main_schedule(initlr=initlr,
                                                        mode=mode,
                                                        value=value,
                                                        duration=duration,
                                                        total_epoch=total_epoch), verbose=1)

def callback_evaluate(generator, eval_steps, is_patch):
    return Evaluate(generator=generator, eval_steps=eval_steps, is_patch=is_patch, verbose=1)


lines = open("input").readlines()
cnt = 0
for l in lines:
    ws = l.split(" ")
    lo = int(ws[0].split("-")[0])
    hi = int(ws[0].split("-")[1])
    c = ws[1][0:1]
    w = ws[2]
    part = 0
    if w[lo-1] == c:
        part += 1
    if w[hi-1] == c:
        part += 1
    if part == 1:
        cnt += 1
    '''
    if w.count(c) >= lo and w.count(c) <= hi:
        cnt += 1
    '''
print(cnt)


from __future__ import division

import torch
import numpy as np
import cv2

def unique(tensor):
    """
    Returns all unique values in tensor
    """

    tensor_np = tensor.cpu().numpy()
    unique_np = np.unique(tensor_np)
    unique_tensor = torch.from_numpy(unique_np)

    tensor_res = tensor.new(unique_tensor.shape)
    tensor_res.copy_(unique_tensor)
    return tensor_res

def bbox_iou(box1, box2):
    """
    Returns the IoU of two bounding boxes
    """

    # get bbox coords
    b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3]
    b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]

    # get coordinates of intersection rectangle
    inter_rect_x1 = torch.max(b1_x1, b2_x1)
    inter_rect_y1 = torch.max(b1_y1, b2_y1)
    inter_rect_x2 = torch.min(b1_x2, b2_x2)
    inter_rect_y2 = torch.min(b1_y2, b2_y2)

    # calculate intersection area
    inter_area = torch.clamp(
        inter_rect_x2 - inter_rect_x1 + 1,
        min=0
    ) * torch.clamp(
        inter_rect_y2 - inter_rect_y1 + 1,
        min=0
    )

    # calculate union area
    b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1)
    b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1)
    union_area = b1_area + b2_area - inter_area

    # return IoU
    return (inter_area / union_area)

def predict_transform(prediction, inp_dim, anchors, num_classes, CUDA=False):
    """
    Transforms all predictions across the 3 scales into a consistent siz
    """
    batch_size = prediction.size(0)
    stride = inp_dim // prediction.size(2)
    grid_size = inp_dim // stride
    bbox_attrs = 5 + num_classes
    num_anchors = len(anchors)

    # transform predictions to 2d
    prediction = prediction.view(
        batch_size, bbox_attrs * num_anchors, grid_size*grid_size)
    prediction = prediction.transpose(1, 2).contiguous()
    prediction = prediction.view(
        batch_size, grid_size*grid_size*num_anchors, bbox_attrs)

    # adjust anchor box sizes for detection layer sizes
    anchors = [(a[0]/stride, a[1]/stride) for a in anchors]

    # Sigmoid the coords and confidence scores
    prediction[:, :, 0] = torch.sigmoid(prediction[:, :, 0])
    prediction[:, :, 1] = torch.sigmoid(prediction[:, :, 1])
    prediction[:, :, 4] = torch.sigmoid(prediction[:, :, 4])

    # Add grid offsets to center coords prediction
    grid = np.arange(grid_size)
    a, b = np.meshgrid(grid, grid)

    x_offset = torch.FloatTensor(a).view(-1, 1)
    y_offset = torch.FloatTensor(b).view(-1, 1)

    if CUDA:
        x_offset = x_offset.cuda()
        y_offset = y_offset.cuda()

    x_y_offset = torch.cat((x_offset, y_offset), 1).repeat(
        1, num_anchors).view(-1, 2).unsqueeze(0)

    prediction[:, :, :2] += x_y_offset

    # apply anchors to dimensions of bounding box
    anchors = torch.FloatTensor(anchors)

    if CUDA:
        anchors = anchors.cuda()

    anchors = anchors.repeat(grid_size*grid_size, 1).unsqueeze(0)
    prediction[:, :, 2:4] = torch.exp(
        prediction[:, :, 2:4])*anchors  # log space transforms

    # apply sigmoid to class scores
    prediction[:, :, 5:5 +
               num_classes] = torch.sigmoid((prediction[:, :, 5:5+num_classes]))

    # resize detectoin map to the size of the input image
    prediction[:, :, :4] *= stride

    return prediction

def write_results(prediction, confidence, num_classes, nms_conf=0.4):
    """
    Take predictions, prepare them, and then perform NMS on them
    """

    conf_mask = (prediction[:, :, 4] > confidence).float().unsqueeze(2)
    prediction = prediction * conf_mask  # if below confidence threshold, set to 0

    # create new tensor of same shape and value as prediction
    box_corner = prediction.new(prediction.shape)
    box_corner[:, :, 0] = (prediction[:, :, 0] - prediction[:, :, 2]/2)
    box_corner[:, :, 1] = (prediction[:, :, 1] - prediction[:, :, 3]/2)
    box_corner[:, :, 2] = (prediction[:, :, 0] + prediction[:, :, 2]/2)
    box_corner[:, :, 3] = (prediction[:, :, 1] + prediction[:, :, 3]/2)
    prediction[:, :, :4] = box_corner[:, :, :4]

    batch_size = prediction.size(0)
    write = False

    for ind in range(batch_size):
        image_pred = prediction[ind]  # individual image tensor

        # reduce to max class only instead of all classes
        # index and value, max along  dim 1 of (0,1) (max class)
        max_conf, max_conf_score = torch.max(image_pred[:, 5:5+num_classes], 1)
        max_conf = max_conf.float().unsqueeze(1)
        max_conf_score = max_conf_score.float().unsqueeze(1)
        # bounding box, class, class confidence
        seq = (image_pred[:, :5], max_conf, max_conf_score)
        image_pred = torch.cat(seq, 1)

        # remove zeroed bounding boxes from before
        non_zero_index = (torch.nonzero(image_pred[:, 4]))
        try:
            image_pred_ = image_pred[non_zero_index.squeeze(), :].view(-1, 7)
        except:
            continue  # if no detections happen

        if image_pred_.shape[0] == 0:
            continue  # if no detections happen

        # get various classes that were detected
        # -1 holds class index, find all unique class indexes
        img_classes = unique(image_pred_[:, -1])

        # perform NMS classwise
        for c in img_classes:  # for every class present in the detections

            # get the detections of the current class
            cls_mask = image_pred_ * \
                (image_pred_[:, -1] == c).float().unsqueeze(1)
            class_mask_index = torch.nonzero(cls_mask[:, -2]).squeeze()
            image_pred_class = image_pred_[class_mask_index].view(-1, 7)

            # sort detections such that the max objectness score is at the top
            conf_sort_index = torch.sort(
                image_pred_class[:, 4], descending=True)[1]
            image_pred_class = image_pred_class[conf_sort_index]
            idx = image_pred_class.size(0)  # num of detections

            # NMS
            for i in range(idx):
                # get all IoUs of the boxes after the current one in the loop
                # since we sorted bboxes by class confidence, any box with an index after the current
                # will have a smaller class confidence score
                try:
                    ious = bbox_iou(image_pred_class[i].unsqueeze(
                        0), image_pred_class[i+1:])
                except ValueError:
                    break
                except IndexError:
                    break

                # zero all detections with IoU > threshold
                iou_mask = (ious < nms_conf).float().unsqueeze(1)
                image_pred_class[i+1:] *= iou_mask

                # remove zero entries
                # here a zero entry is a bounding box that has too high an IoU with the current
                # bbox and since it is atleast i+1 indexed, it will have a lower confidence score
                non_zero_ind = torch.nonzero(image_pred_class[:, 4]).squeeze()
                image_pred_class = image_pred_class[non_zero_ind].view(-1, 7)

            # write results
            batch_ind = image_pred_class.new(
                image_pred_class.size(0), 1).fill_(ind)
            seq = batch_ind, image_pred_class

            if not write:
                output = torch.cat(seq, 1)
                write = True
            else:
                out = torch.cat(seq, 1)
                output = torch.cat((output, out))

    # try to return something, otherwise return a 0
    try:
        return output
    except Exception:
        return 0

def load_classes(namesFile):
    """
    Return a list of names from a class file (classnames separated by \n)
    """

    fp = open(namesFile, "r")
    names = fp.read().split("\n")[:-1]
    return names

def letterbox_image(img, inp_dim):
    """
    Resize image with unchanged aspect ratio using padding
    """

    img_w, img_h = img.shape[1], img.shape[0]
    w, h = inp_dim
    new_w = int(img_w * min(w/img_w, h/img_h))
    new_h = int(img_h * min(w/img_w, h/img_h))
    resized_img = cv2.resize(
        img, (new_w, new_h), interpolation=cv2.INTER_CUBIC)

    # fill a new array of size x, y, 3 with 128 values
    canvas = np.full((inp_dim[1], inp_dim[0], 3), 128)
    canvas[
        (h-new_h)//2:(h-new_h)//2 + new_h,
        (w-new_w)//2:(w-new_w)//2 + new_w,
        :
    ] = resized_img

    return canvas

def prep_image(img, inp_dim):
    """
    Convert image from openCV format to Pytorch format
    """

    img = (letterbox_image(img, (inp_dim, inp_dim)))
    img = img[:, :, ::-1].transpose(2, 0, 1).copy()
    img = torch.from_numpy(img).float().div(
        255.0).unsqueeze(0)  # convert to torch and normalise
    return img

def setController(buzz, pos, ser):
    """
    Sets servo angle to pos and buzzer state to buzz on serial port ser
    """
    s = "{0:03d}{1}".format(int(pos), buzz) # Add trailing zeros to make 3 digit number
    print(s)
    b = bytes(s, 'utf-8')

    ser.write(b) # Convert the decimal number to ASCII then send it to the Arduino
    #ser.readline() # block until arduino responds
    ser.flushInput()
    ser.flushOutput()


import json
import requests
import urllib
import config
import fractions
import logging
import time
from CFDCamperLogo import LOGO

url = config.URL
headers = config.HEADERS
auth = config.AUTH
UNIT = 10**8
FEE_PER_KB=20000 #Increase our chance of getting into the block on time
FEED_ADDRESS = '1E7qexXyupzYqx9R6rbUt2PciyTZ35MBDf' #Feed Address to Camp
MIN_TARGET_SIZE = 8 * UNIT #Minimum wager amount of target bet
MIN_ATTACK_DISTANCE = 900 #in seconds, time before next broadcast to start attack
MIN_ATTACK_SIZE = .75 #minimum normalized difference between broadcasts to attack for
SAFETY_CONSTANT= .9 #Adjust for how much the price might change between the matching attack and the broadcast block for small differences
SAFETY_RATIO = .75 #percent based adjustment for large movements
SAFETY_THRESHOLD = 6 #Threshold for switching between safety_constant and safety_ratio
MY_ADDRESSES = ['19r2xo2iZ8UzxxgvjVcXkVFw7GhypQLnxF','1A6pex2MMEqJtRdwGPwZmEQLJ7Jw65QCBz', '1GwusE6uPNpjNUxV3LoZCwEsdY93mbyoxp', '1FvrgmfKAr3Uc7KeACMk6DFwbJggXR8FxV', '1CFD35UfrKn92rnHQZj213Be9qjBssEEr3']#['mhtp9Fpt1Citcfhua34DrhZQMTwMF2qS7V', 'moSG49871W5D1CmscFJjJgYyH2BnFwLpJD', 'mtAMogTagoRwJkcDxduBY8iMnxxehofqrX', 'n4No9RNhAGmkJT54LmcsenagfrGXbvm8r6', 'msRd43qcfz7vfmRAJks2f58caiSzS94Hoa', 'mrwDQYWweppb7siEDm7w2sbjpBDWX7gEJm']
LAST_BROADCAST = None
LAST_BROADCAST_TIMESTAMP= None
CAST_INTERVAL = 7200 #Either gotten from enhanced info or established manually
BET_TYPE_NAME = {0: 'Bull', 1: 'Bear'}
USE_LOCAL_TIMESTAMP = True #use Timestamp from when the broadcast is received, or the one contained in the broadcast

F = fractions.Fraction

logging.basicConfig(
    level = logging.INFO,
    format = '%(asctime)s %(levelname)s %(message)s',
)
class LoopyAccessList(list):
    def __init__(self, *args, **kwargs):
        list.__init__(self, *args)
        self.counter = 0
    def get(self, ):
        self.counter += 1
        key = self.counter % len(self)
        return self[key - 1]

MY_ADDRESSES = LoopyAccessList(MY_ADDRESSES)

def qNum(num_items):
   return ','.join('?' for i in range(num_items))

    #Don't attack you're own open wagers that didn't match
def getOpenBets():
    bindings = [FEED_ADDRESS] + MY_ADDRESSES
    query = 'SELECT * FROM bets WHERE (status == "open") AND (feed_address == ?) AND (source NOT IN (%s))' %(qNum(len(MY_ADDRESSES)))
    payload = {'method': 'sql', 'params': {'query': query, 'bindings': bindings}, 'jsonrpc': '2.0', 'id': 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response['result']
def trim(x):
    return round(x / 10**8, 4)

def getLastBroadcast():
    bindings = [FEED_ADDRESS]
    query= 'SELECT * FROM broadcasts WHERE (status = "valid" AND source = ?) ORDER BY tx_index DESC'
    payload = {'method': 'sql', 'params': {'query': query, 'bindings': bindings}, 'jsonrpc': '2.0', 'id': 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response['result'][0]

#for Estimating the value of the next broadcast #Must be tailored to the specific Feed
def getRealTimePrice():
    price, timestamp = bitstampTicker()  #ticker to Use
    return price

def btcTicker():
    data = json.loads(urllib.request.urlopen('https://api.coindesk.com/v1/bpi/currentprice.json').read().decode('utf-8'))
    dataPrice= float(data['bpi']['USD']['rate'])
    dataTime = data['time']['updated']
    return dataPrice, dataTime

def bitstampTicker():
    data = json.loads(urllib.request.urlopen('https://www.bitstamp.net/api/ticker/').read().decode('utf-8'))
    dataPrice = float(data['last'])
    dataTime = data['timestamp']
    return dataPrice, dataTime

def doBet(params):
    default_params = {'allow_unconfirmed_inputs': True, 'target_value':0}
    params.update(default_params)
    payload = { "method": "do_bet", "params": params, "jsonrpc": "2.0", "id": 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response

def getRunningInfo():
    payload = { "method": "get_running_info", "params": {}, "jsonrpc": "2.0", "id": 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response['result']

print(LOGO)
STARTUP = True
target_tracked = {}
while True:
    if not STARTUP:
        logging.info("sleeping for 120 seconds")
        time.sleep(120)
    STARTUP= False
    try:
        current_block = getRunningInfo()['last_block']['block_index']
    except:
        logging.info("Could not connect to Counterpartyd RPC")
        continue
    last_broadcast = getLastBroadcast()
    if not LAST_BROADCAST or (last_broadcast['tx_hash'] != LAST_BROADCAST['tx_hash']):
        LAST_BROADCAST = last_broadcast
        initial_value = LAST_BROADCAST['value']
        if USE_LOCAL_TIMESTAMP and LAST_BROADCAST_TIMESTAMP:
            LAST_BROADCAST_TIMESTAMP = time.time()
        else:
            LAST_BROADCAST_TIMESTAMP = last_broadcast['timestamp']
        logging.info("Added New Broadcast, Value: {0}, Time: {1}".format(initial_value, time.ctime(LAST_BROADCAST_TIMESTAMP)))
    current_time = time.time()
    estimate_till_next_broadcast = (CAST_INTERVAL - (current_time - LAST_BROADCAST_TIMESTAMP ))
    logging.info("Estimated time till next broadcast: {}".format(estimate_till_next_broadcast))
    if estimate_till_next_broadcast > MIN_ATTACK_DISTANCE: continue
    open_bets  = getOpenBets()
    logging.info("Found {0} open bets at Feed {1}".format(len(open_bets), FEED_ADDRESS))
    if not open_bets: continue
    target_bets = {}
    for bet in open_bets:
        if bet['wager_remaining'] < MIN_TARGET_SIZE: continue
        if not bet['bet_type'] in [0,1]: continue
        if (bet['counterwager_quantity'] / bet['wager_quantity']) > 5: continue
        bet_expire_remaining = bet['expiration'] - (current_block - bet['block_index'])
        if (bet_expire_remaining - 1) <  (estimate_till_next_broadcast / (60 * 10)): continue
        target_bets[bet['tx_hash']] = bet
    if not target_bets: continue
    logging.info("Found {0} targets at Feed {1}".format(len(open_bets), FEED_ADDRESS))
    current_price = getRealTimePrice()
    price_difference = current_price - initial_value
    if SAFETY_CONSTANT< price_difference < SAFETY_THRESHOLD:
        safe_difference = (price_difference - SAFETY_CONSTANT)
    elif -SAFETY_THRESHOLD < price_difference < -SAFETY_CONSTANT:
        safe_difference = (price_difference + SAFETY_CONSTANT)
    elif -SAFETY_CONSTANT< price_difference < SAFETY_CONSTANT:
        safe_difference = 0
    else:
        safe_difference = (price_difference * SAFETY_RATIO)
    logging.info("Initital Value {0}, Real time price {1}, Price difference {2}, Estimated safe difference {3}".format(initial_value, current_price, price_difference, safe_difference))
    if abs(safe_difference) < MIN_ATTACK_SIZE: continue
    for target in target_bets.values():
        if (target['bet_type'] == 0 and safe_difference > 0) or (target['bet_type'] == 1 and safe_difference < 0):
            logging.info("Target type {0}, Estimated Safe Difference {1} --Skipping".format(BET_TYPE_NAME[target['bet_type']], safe_difference))
            continue
        else:
            logging.info("Target type {0}, Estimated Safe Difference {1} --Targetting".format(BET_TYPE_NAME[target['bet_type']], safe_difference))
        try:
            if (time.time() - target_tracked[target['tx_hash']]['timestamp']) < 20*60: pass
            else: raise Exception
        except:
            target_tracked[target['tx_hash']] = {'timestamp': time.time(), 'estimated_wager_remaining': target['wager_remaining']}
        print("target_wager_remaining %s" %target_tracked[target['tx_hash']]['estimated_wager_remaining'])
        while target_tracked[target['tx_hash']]['estimated_wager_remaining'] >= 0:
            time.sleep(2)
            target_inverse_odds = F(target['counterwager_quantity']/ target['wager_quantity'])
            normalized_leverage = target['leverage']/ 5040.0
            if target_tracked[target['tx_hash']]['estimated_wager_remaining'] > abs(safe_difference * UNIT * normalized_leverage):
                wager_quantity = abs(round((safe_difference * UNIT) * target_inverse_odds * normalized_leverage))
                counterwager_quantity = abs(round((safe_difference * UNIT * normalized_leverage)))
            else:
                wager_quantity = abs(round(target_tracked[target['tx_hash']]['estimated_wager_remaining'] * target_inverse_odds * normalized_leverage))
                counterwager_quantity = abs(round(target_tracked[target['tx_hash']]['estimated_wager_remaining'] * normalized_leverage))
            leverage = target['leverage']
            deadline = target['deadline']
            bet_type = 0 if target['bet_type'] == 1 else 1
            expiration = 2
            source= MY_ADDRESSES.get()
            params = {'fee_per_kb': FEE_PER_KB, 'source': source, 'expiration': expiration, 'bet_type': bet_type, 'deadline': deadline, 'leverage': leverage, 'counterwager_quantity': counterwager_quantity, 'wager_quantity': wager_quantity, 'feed_address': FEED_ADDRESS}
            print(params)
            try:
                print('trying')
                tx_hash = doBet(params)['result']
            except:
                print('EXCEPT')
                continue
            if tx_hash:
                target_tracked[target['tx_hash']]['estimated_wager_remaining'] = target_tracked[target['tx_hash']]['estimated_wager_remaining'] - wager_quantity
                logging.info("ATTACK: Target Address {0}, Estimated wager_remaining {1}, Actual wager_remaining {2}, Wager_quantity {3}".format(target['source'], trim(target_tracked[target['tx_hash']]['estimated_wager_remaining']), trim(target['wager_remaining']), trim(wager_quantity)))
                logging.info("ATTACK: HASH {}".format(tx_hash))


from unittest.mock import Mock, call

import pytest

from uttut import NOT_ENTITY
from uttut.elements import Datum, Intent, Entity
from ..ordinal_label import OrdinalLabel

TRANSFORMATION_PAIRS = [
    (
        {
            'utterance': '你好',
            'intent': {'names': ['HI']},
        },
        Datum(utterance='你好', intents=[Intent(0)]),
    ),
    (
        {
            "utterance": "我想喝珍奶半糖",
            "intent": {
                "names": ["ORDER"],
            },
            "entities": [
                {
                    "name": "ITEM",
                    "start": 3,
                    "end": 5,
                    "replacements": ["拿鐵", "多多綠", "紅茶"],
                },
                {
                    "name": "SUGAR",
                    "start": 5,
                    "end": 7,
                    "replacements": ["無糖", "全糖"],
                },
            ],
        },
        Datum(
            utterance='我想喝珍奶半糖',
            intents=[Intent(2)],
            entities=[
                Entity(1, '珍奶', 3, 5, ["拿鐵", "多多綠", "紅茶"]),
                Entity(2, '半糖', 5, 7, ["無糖", "全糖"]),
            ],
        ),
    ),
]

@pytest.fixture(scope='function')
def tx():
    intent2index = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    entity2index = {'ITEM': 1, 'SUGAR': 2}
    not_entity_index = 0
    tx = OrdinalLabel(intent2index, entity2index, not_entity_index)
    return tx

@pytest.yield_fixture
def mocked_OrdinalLabel():
    orig_checker = OrdinalLabel.is_valid_mapping
    OrdinalLabel.is_valid_mapping = Mock(return_value=True)
    yield OrdinalLabel
    OrdinalLabel.is_valid_mapping = orig_checker

def test_ordinal_label_init(mocked_OrdinalLabel):
    intent2index = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    entity2index = {'DRINK': 0, 'SIZE': 1}
    not_entity_index = 2
    tx = mocked_OrdinalLabel(intent2index, entity2index, not_entity_index)
    assert tx._intent2index == intent2index
    assert tx._entity2index == entity2index
    assert tx._not_entity_index == not_entity_index

    assert tx.not_entity_index == not_entity_index

    actual_calls = mocked_OrdinalLabel.is_valid_mapping.call_args_list
    expected_calls = [
        call(intent2index),
        call({**entity2index, NOT_ENTITY: 2}),
    ]
    assert expected_calls == actual_calls

def test_ordinal_label_init_raise_if_intent2index_not_valid():
    # setup
    orig_checker = OrdinalLabel.is_valid_mapping
    OrdinalLabel.is_valid_mapping = Mock(side_effect=[False, True])

    intent2index = {}
    entity2index = {}
    with pytest.raises(ValueError):
        OrdinalLabel(intent2index, entity2index)

    # tear down
    OrdinalLabel.is_valid_mapping = orig_checker

def test_ordinal_label_init_raise_if_entity2index_not_valid():
    # setup
    orig_checker = OrdinalLabel.is_valid_mapping
    OrdinalLabel.is_valid_mapping = Mock(side_effect=[True, False])

    intent2index = {}
    entity2index = {}
    with pytest.raises(ValueError):
        OrdinalLabel(intent2index, entity2index)

    # tear down
    OrdinalLabel.is_valid_mapping = orig_checker

def test_ordinal_label_is_valid_mapping():
    correct_mapping = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    duplicate_mapping = {'HI': 0, 'BYE': 1, 'ORDER': 1}
    non_ordinal_mapping = {'HI': 0, 'BYE': 1, 'ORDER': 3}
    mapping_with_negative = {'HI': -1, 'BYE': 0, 'ORDER': 1}
    assert OrdinalLabel.is_valid_mapping(correct_mapping)
    assert not OrdinalLabel.is_valid_mapping(duplicate_mapping)
    assert not OrdinalLabel.is_valid_mapping(non_ordinal_mapping)
    assert not OrdinalLabel.is_valid_mapping(mapping_with_negative)

@pytest.mark.parametrize('expected, datum', TRANSFORMATION_PAIRS)
def test_ordinal_label_humanize(expected, datum, tx):
    actual = tx.humanize(datum)
    assert set(actual.keys()) == set(expected.keys())
    assert actual['utterance'] == expected['utterance']
    assert set(actual['intent']['names']) == set(expected['intent']['names'])
    assert len(actual.get('entities', [])) == len(expected.get('entities', []))

    if len(actual.get('entities', [])) > 0:
        for actual_entity, expected_entity in zip(actual['entities'], expected['entities']):
            assert actual_entity['name'] == expected_entity['name']
            assert actual_entity['start'] == expected_entity['start']
            assert actual_entity['end'] == expected_entity['end']
            actual_replacements = set(actual_entity.get('replacements', []))
            expected_replacements = set(expected_entity.get('replacements', []))
            assert actual_replacements == expected_replacements

@pytest.mark.parametrize('raw_dict, datum', TRANSFORMATION_PAIRS)
def test_ordinal_label_machanize(raw_dict, datum, tx):
    assert tx.machanize(raw_dict) == datum

def test_ordinal_label_serialization():
    intent2index = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    entity2index = {'DRINK': 1, 'SIZE': 2}
    tx = OrdinalLabel(intent2index, entity2index)

    serialized = tx.serialize()
    loaded_tx = OrdinalLabel.deserialize(serialized)

    assert tx._intent2index == loaded_tx._intent2index
    assert tx._entity2index == loaded_tx._entity2index

@pytest.fixture(scope='function')
def raw_data():
    return {
        "data": [
            {
                "utterance": "你好",
                "intent": {
                    "names": ["GREETINGS"],
                },
            },
            {
                "utterance": "我想喝珍奶半糖",
                "intent": {
                    "names": ["ORDER"],
                },
                "entities": [
                    {
                        "name": "ITEM",
                        "start": 3,
                        "end": 5,
                        "replacements": ["拿鐵", "多多綠", "紅茶"],
                    },
                    {
                        "name": "SUGAR",
                        "start": 5,
                        "end": 7,
                        "replacements": ["無糖", "全糖"],
                    },
                ],
            },
        ],
    }

def test_ordinal_from_raw_dictionary(raw_data):
    tx = OrdinalLabel.from_raw_dictionary(raw_data)
    # indexes are based on the appearance order
    assert tx._intent2index == {"GREETINGS": 0, "ORDER": 1}
    assert tx._entity2index == {"ITEM": 1, "SUGAR": 2}

def test_ordinal_from_raw_dictionary_with_custom_not_entity_index(raw_data):
    tx = OrdinalLabel.from_raw_dictionary(raw_data, not_entity_index=1)
    # indexes are based on the appearance order
    assert tx._intent2index == {"GREETINGS": 0, "ORDER": 1}
    assert tx._entity2index == {"ITEM": 0, "SUGAR": 2}

def test_ordinal_from_raw_dictionary_with_custom_not_entity_index_2(raw_data):
    tx = OrdinalLabel.from_raw_dictionary(raw_data, not_entity_index=2)
    # indexes are based on the appearance order
    assert tx._intent2index == {"GREETINGS": 0, "ORDER": 1}
    assert tx._entity2index == {"ITEM": 0, "SUGAR": 1}

@pytest.mark.parametrize('wrong_entity_index', [0., 1., 100., 0.1, '0', '0.1'])
def test_ordinal_from_raw_dictionary_raise_with_wrong_type(wrong_entity_index):
    raw_data = {'data': []}
    with pytest.raises(TypeError):
        OrdinalLabel.from_raw_dictionary(raw_data, not_entity_index=wrong_entity_index)


# ------------------------------------------
# DESCRIPTION OF MODULE
# ------------------------------------------

"""
CamPong Score class

It stores the score of a bat and may be used to display the
corresponding text on the screen
"""

# ------------------------------------------
# IMPORTS
# ------------------------------------------

try:
    import os
    os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
    import pygame
    import math
    from Constants import *
except ImportError as err:
    print ("Error: couldn't load module" + str(err) + ". Exiting...")
    exit()

# ------------------------------------------
# CLASSES DEFINITIONS
# ------------------------------------------

class Score(pygame.sprite.Sprite):
    """
    Attributes:
        - font:Font
        - text:String
        - image:Surface
        - rect:Rect. Determines position and hitbox
        - position:(Int,Int). (X,Y)
        - area:Rect. Screen the object is moving across
        - sprite:Sprite
    Methods: __init__, update, calcnewpos
    """

    TEXT_SIZE = 20

    def __init__(self, position_init):
        pygame.sprite.Sprite.__init__(self)
        self.font = pygame.font.Font(None, 36)
        self.text = "0 - 0"
        # image (appearance)
        self.image = self.font.render(self.text, True, COLOUR_WHITE)
        # rect (hitbox)
        self.rect = self.image.get_rect()
        self.position = position_init
        self.rect.center = self.position
        pygame.draw.rect(self.image, COLOUR_WHITE, self.rect)
        # area that contains the text (i.e. the board itself)
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        # sprite
        self.sprite = pygame.sprite.RenderPlain(self)

    def update(self, score1, score2, screen, background):
        self.text = str(score1) + " - " + str(score2)
        self.image = self.font.render(self.text, True, COLOUR_WHITE)
        self.rect = self.image.get_rect()
        self.rect.center = self.position
        screen.blit(background, self.rect, self.rect)
        self.sprite.draw(screen)

    def calcnewpos(self,rect,vector):
        (angle,z) = vector
        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
        return rect.move(dx,dy)