import logging
import os
import subprocess
from audio import HandBrakeAudioInfo

log_format = '[%(levelname)-s] %(filename)-s %(funcName)-4s(%(lineno)-s) - %(message)-4s'
logging.basicConfig(level=logging.INFO, format=log_format)

class FFmpegStreamInfo(object):
    def __init__(self, stream_index, codec_type, codec_name, language_code, metadata):
        self.stream_index = stream_index
        self.codec_type = codec_type
        self.codec_name = codec_name
        self.language_code = language_code
        self.metadata = metadata

class HandBrake():
    def parse_handbrake_scan_output(output):
        lines = output.splitlines()
        hb_audio_tracks = None
        hb_subtitle_tracks = None
        ff_audio_streams = None
        ff_subtitle_streams = None
        i = 0
        while i < len(lines):
            if lines[i].startswith("Input #0, "):
                logging.debug("Found FFmpeg stream info")
                i, ff_audio_streams, ff_subtitle_streams = HandBrake().parse_ffmpeg_stream_info(lines, i)
                message_format = "FFmpeg: %d audio track(s), %d subtitle track(s)"
                logging.debug(message_format, len(ff_audio_streams), len(ff_subtitle_streams))
                continue
            if lines[i] == "  + audio tracks:":
                logging.debug("Found HandBrake audio track info")
                i, hb_audio_tracks = HandBrake().parse_handbrake_track_info(lines, i, HandBrakeAudioInfo)
                logging.debug("HandBrake: %d audio track(s)", len(hb_audio_tracks))
                continue
            if lines[i] == "  + subtitle tracks:":
                logging.debug("Found HandBrake subtitle track info")
                i, hb_subtitle_tracks = HandBrake().parse_handbrake_track_info(lines, i, HandBrake().HandBrakeSubtitleInfo)
                logging.debug("HandBrake: %d subtitle track(s)", len(hb_subtitle_tracks))
                continue
            i += 1
        HandBrake().merge_track_titles(hb_audio_tracks, ff_audio_streams)
        HandBrake().merge_track_titles(hb_subtitle_tracks, ff_subtitle_streams)
        return (hb_audio_tracks, hb_subtitle_tracks)

    def merge_track_titles(hb_tracks, ff_streams):
        if not ff_streams:
            return
        assert len(hb_tracks) == len(ff_streams), "Track count mismatch"
        for hb_track, ff_stream in zip(hb_tracks, ff_streams):
            assert hb_track.language_code == ff_stream.language_code, "Track language code mismatch"
            hb_track.title = ff_stream.metadata.get("title")

    def parse_handbrake_track_info(self, output_lines, start_index, info_cls):
        prefix = "    + "
        prefix_len = len(prefix)
        tracks = []
        i = start_index + 1
        while i < len(output_lines) and output_lines[i].startswith(prefix):
            info_str = output_lines[i][prefix_len:]
            info = info_cls(info_str)
            tracks.append(info)
            i += 1
        return (i, tracks)

    def parse_scan_output(self, input):
        lines = input.splitlines()
        i = 0
        while i < len(lines):
            if lines[i] == "  + audio tracks:":
                logging.info("Found HandBrake audio track info")
                logging.info("Line: %s" % (lines[i]))
                i, hb_audio_tracks = HandBrake().parse_handbrake_track_info(lines, i, HandBrakeAudioInfo)
                logging.info("HandBrake: %d audio track(s)", len(hb_audio_tracks))
                # continue
            i += 1
        return True

    def scan_file(self, handbrake_exe, filename):
        logging.info("Using Handbrake exe: %s" % (handbrake_exe))
        logging.info("Checking on file: %s" % (filename))
        output = subprocess.check_output([
            handbrake_exe,
            "-i", filename,
            "--scan"
        ], stderr=subprocess.STDOUT)
        return output.decode("utf-8")

    def check_handbrake_executable(self, file_path):
        if not os.path.isfile(file_path):
            return False
        message_format = "Found HandBrakeCLI binary at '%s'"
        if not os.access(file_path, os.X_OK):
            message_format += ", but it is not executable"
            logging.info(message_format, file_path)
            return False
        logging.info(message_format, file_path)
        return True

    def find_handbrake_executable_in_path(self, name):
        path_env = os.environ.get("PATH", os.defpath)
        path_env_split = path_env.split(os.pathsep)
        path_env_split.insert(0, os.path.abspath(os.path.dirname(__file__)))
        for dir_path in path_env_split:
            file_path = os.path.join(dir_path, name)
            if HandBrake().check_handbrake_executable(file_path):
                return file_path
        return None


import Trie
from Piece import Piece

class PieceTableEditor:

   #Initialization
    def __init__(self, document):
        self.textLength = len(document)
        self.original = document
        self.buffer = ""
        self.pieces = [Piece(False, 0, len(document))]
        self.dictionary = Trie.Trie()
        # On windows, the dictionary can often be found at:
        # C:/Users/{username}/AppData/Roaming/Microsoft/Spelling/en-US/default.dic
        with open("C:/Users/kirti/AppData/Roaming/Microsoft/Spelling/en-US/default.dic") as input_dictionary:
            for line in input_dictionary:
                words = line.strip().split(" ")
                for word in words:
                    self.dictionary.insert(word.lower())
        self.pasteText = ""

    #Insert text at a particular index
    def insert(self, text, index):
        if not len(text):
            return
        # Finding piece that overlaps the given index
        pieceIndex, pieceOffset = self.getPieceIndexAndOffset(index)
        current = self.pieces[pieceIndex]

        # Append text to added buffer
        added_offset = len(self.buffer)
        self.buffer += text
        self.textLength += len(text)

        # If insertion is at the end and the piece points to the end of the add buffer,
        # just increase the length
        if (current.inBuffer
            and pieceOffset == current.offset + current.length == added_offset):
            current.length += len(text)
            return

        # Spliting into three pieces
        insertPiece = []
        insertPiece.append( Piece(current.inBuffer, current.offset, pieceOffset - current.offset))
        insertPiece.append(Piece(True, added_offset, len(text)))
        insertPiece.append(Piece(current.inBuffer,pieceOffset,current.length - (pieceOffset - current.offset)))
        insertPiece = list(filter(lambda piece: piece.length > 0, insertPiece))
        self.pieces = self.replacePieces(pieceIndex, 1, insertPiece)

    def getPieceIndexAndOffset(self, index):
        remainingOffset = index
        for i in range(len(self.pieces)):
            piece = self.pieces[i]
            if remainingOffset <= piece.length:
                return (i, piece.offset + remainingOffset)
            remainingOffset -= piece.length

    def replacePieces(self, start, replace_count, items):
        return self.pieces[:start] + items + self.pieces[start + replace_count:]

    def delete(self, index, length):

        if length == 0:
            return
        if length < 0:
            self.delete(index + length, -length)
            return
        if index < 0:
            raise IndexError("Text index out of range")

        # Get overlapping pieces
        startIndex, startOffset = self.getPieceIndexAndOffset(index)
        stopIndex, stopOffset = self.getPieceIndexAndOffset(index + length)
        self.textLength -= length

        # If single piece, check if delete is at the start or end the piece
        if startIndex == stopIndex:
            piece = self.pieces[startIndex]

            if startOffset == piece.offset:
                piece.offset += length
                piece.length -= length
                return
            elif stopOffset == piece.offset + piece.length:
                piece.length -= length
                return

        startPiece = self.pieces[startIndex]
        endPiece = self.pieces[stopIndex]

        # Spliting into two pieces
        deletePieces = []
        deletePieces.append(Piece(startPiece.inBuffer,startPiece.offset,startOffset - startPiece.offset))
        deletePieces.append(Piece(startPiece.inBuffer,startPiece.offset,startOffset - startPiece.offset,))
        deletePieces.append(Piece(endPiece.inBuffer,stopOffset,endPiece.length - (stopOffset - endPiece.offset)))

        deletePieces = list(filter(lambda piece: piece.length > 0, deletePieces))

        delete_count = stopIndex - startIndex + 1
        self.pieces = self.replacePieces(
            startIndex, delete_count, deletePieces
        )

    def get_text(self):

        document = ""
        for piece in self.pieces:
            if piece.inBuffer:
                document += self.buffer[piece.offset:piece.offset + piece.length]
            else:
                document += self.original[piece.offset:piece.offset + piece.length]
        return document

    #Getting string of length at an index
    def getString(self, index, length):
        if length < 0:
            return self.getString(index + length, -length)

        document = ""

        # Get overlapping pieces
        startIndex, startOffset = self.getPieceIndexAndOffset(index)
        stopIndex, stopOffset = self.getPieceIndexAndOffset(index + length)

        startPiece = self.pieces[startIndex]
        buffer = self.buffer if startPiece.inBuffer else self.original

        # If single piece, return text from piece
        if startIndex == stopIndex:
            document = buffer[startOffset:startOffset + length]
        else:
            document = buffer[startOffset:startPiece.offset + startPiece.length]
            for i in range(startIndex + 1, stopIndex + 1):
                cur_piece = self.pieces[i]
                buffer = self.buffer if cur_piece.inBuffer else self.original

                # If the ending piece, only add remaining length to the string
                if i == stopIndex:
                    document += buffer[cur_piece.offset:stopOffset]
                else:
                    document += buffer[cur_piece.offset:cur_piece.offset + cur_piece.length]

        return document

    def cut(self, i, j):
        self.pasteText = self.getString(i, j-i)
        self.delete(i, j-i)

    def copy(self, i, j):
        self.pasteText = self.getString(i, j-i)

    def paste(self, i):
        self.insert(self.pasteText, i)

    def misspellings(self):
        result = 0
        doc = self.get_text()
        doc = set(doc.split(" "))
        for word in doc:
            if not self.dictionary.search(word):
                result = result + 1
        return result

import timeit

class EditorBenchmarker:
    new_editor_case = """
from __main__ import PieceTableEditor
s = PieceTableEditor("{}")"""

    editor_cut_paste = """
for n in range({}):
    if n%2 == 0:
        s.cut(3, 5)
    else:
        s.paste(2)"""

    editor_copy_paste = """
for n in range({}):
    if n%2 == 0:
        s.copy(1, 3)
    else:
        s.paste(2)"""

    editor_get_text = """
for n in range({}):
    s.get_text()"""

    editor_mispellings = """
for n in range({}):
    s.misspellings()"""

    def __init__(self, cases, N):
        self.cases = cases
        self.N = N
        self.editor_cut_paste = self.editor_cut_paste.format(N)
        self.editor_copy_paste = self.editor_copy_paste.format(N)
        self.editor_get_text = self.editor_get_text.format(N)
        self.editor_mispellings = self.editor_mispellings.format(N)

    def benchmark(self):
        for i, case in enumerate(self.cases):
            print("Evaluating case: {}".format(i+1))
            new_editor = self.new_editor_case.format(case)
            cut_paste_time = timeit.timeit(stmt=self.editor_cut_paste,setup=new_editor,number=1)
            print("{} cut paste operations took {} s".format(self.N, cut_paste_time))
            copy_paste_time = timeit.timeit(stmt=self.editor_copy_paste,setup=new_editor,number=1)
            print("{} copy paste operations took {} s".format(self.N, copy_paste_time))
            get_text_time = timeit.timeit(stmt=self.editor_get_text,setup=new_editor,number=1)
            print("{} text retrieval operations took {} s".format(self.N, get_text_time))
            mispellings_time = timeit.timeit(stmt=self.editor_mispellings,setup=new_editor,number=1)
            print("{} mispelling operations took {} s".format(self.N, mispellings_time))

if __name__ == "__main__":
    s1 = "hello friends"
    s2 = "hello friends" * 1000000
    s3 = "This was  ainteresting question and after looking at the assignment, first problem I could see with the editor was it was performing cut/paste operations on a string. As strings are immutable, each call to those functions were costing us generating a new string alltogther with complexity O(n). Another problem I saw with the editor_mispellings function. As in the dictionary there would be a lot of words and varying length, to check for a correct spelling would take comparisons of length of key size of dictionary. I started with solving that problem first with our existing data strctures. Simply using list instead of string could have been better if there are very insert and delete operations otherwise it would also perform with similar complexity"
    b = EditorBenchmarker([s1, s2, s3], 100)
    b.benchmark()


## time - O(n), space - O(n)
class Solution:
    def maxSubArrayLen(self, nums: List[int], k: int) -> int:
        preSum = [0]
        preSum_dct = {0:-1}
        maxLen = 0
        for i, num in enumerate(nums):
            currSum = preSum[-1]+num
            preSum.append(currSum)
            if currSum - k in preSum_dct:
                maxLen = max(maxLen, i-preSum_dct[currSum - k])
            if currSum not in preSum_dct:
                preSum_dct[currSum] = i
        return maxLen

325. Maximum Size Subarray Sum Equals k _ array.py


'''
Write a Python script to add a key to a dictionary.

Sample Dictionary : {0: 10, 1: 20}
Expected Result : {0: 10, 1: 20, 2: 30}

'''

def dicAdd(dict1, key, value):
    dict1[key] = value
    return dict1

# As sample above
dict1 = {0: 10, 1: 20}
key = 2
value = 30
print(dicAdd(dict1, key, value))


from wordcount_module import *
from operator import itemgetter
import matplotlib.pyplot as plt

word_count('brothers_grimm_fairytales.txt')

dictionary = word_occurence_dict('brothers_grimm_fairytales.txt')

dictionary = sorted(dictionary.items(), key=itemgetter(1))

dictionary = dictionary[-10:-1]

value_list = []

for item in dictionary:
	value_list.append(item[1])

get_sum = sum(value_list)

labels = []
for key in dictionary:
	labels.append(str(key))

total_occurences = []
for value in dictionary:
	total_occurences.append(value)

sizes = []

def calculate_percentage(percentage_list):
	for value in value_list:
		percentage = value / get_sum
		percentage_list.append(percentage)

calculate_percentage(sizes)

fig1, ax1 = plt.subplots()
ax1.pie(sizes, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)
ax1.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.

plt.show()


from tkinter import *
from tkinter import ttk
from tkinter import simpledialog
from PIL import ImageTk, Image
import random
from graph_plot import graph
from bubbleSort import bubble_sort
from bucketsort import bucketSort
from quicksort import quick_sort
from mergesort import merge_sort
from insertionsort import insertionSort
from countsort import countingSort
from radixsort import radixSort
from heapifysort import heapSort
from hybridquicksort import hybrid_quick_sort
from problem_b import problem

root= Tk()
root.title('Sorting Algorithm Project')
image_O = Image.open('data.jpg')
bck_end = ImageTk.PhotoImage(image_O)
root.geometry('900x600+200+80')
lbl = Label(root,image=bck_end)
lbl.place(x=0,y=0)
root.config(bg='#082A46')

# Heading Algorithms
mainlabel = Label(root,text="Algorithms: ", font=("new roman",16,"italic bold"),bg="#E5A5FF",width=10,fg="black",relief=GROOVE,bd=5)
mainlabel.place(x=0,y=0)

# Showing Algorithm options such as Bubble Sort
select_algorithm= StringVar()
algo_menu= ttk.Combobox(root,width=15,font=("new roman",19,"italic bold"),textvariable=select_algorithm,
                        values=['Bubble Sort','Merge Sort','Quick Sort','Insertion Sort','Count Sort','Radix Sort','Heap Sort','Bucket Sort','Hybrid Quick Sort','Problem 8.2.4'])
algo_menu.place(x=145,y=0)
algo_menu.current(0)  #by default bubble sort

# showing data in form of bars in canvas(Black BOX)
def drawData(data,colorArray,mssg=None):
    canvas.delete("all") # it will delete previous data( Data Bars) if this function is  previously run
    if (mssg!= None):
        canvas.create_text(len(mssg)+250,15,text=mssg,font=("new roman",15,"italic bold"),fill="orange")
    canvas_height=450
    canvas_width=870
    x_width= canvas_width / (len(data)+1)
    # spacing between the bars
    offset=10
    spacing_bet_rect=10
    normalised_data=[(i)/max(data) for i in data]  # in order to avoid the small bars
    #creating bar with respect to the number
    for i , height in enumerate(normalised_data):
        x0=i*x_width + offset + spacing_bet_rect
        y0= canvas_height - height * 400  # we have multiplied 400 because we will normalised our values with one
                                        #one formula so that our data won't exceed our canvas
        x1= (i+1)* x_width
        y1= canvas_height

        canvas.create_rectangle(x0,y0,x1,y1,fill=colorArray[i])
        canvas.create_text(x0+2,y0,anchor=SW,text=str(data[i]),font=("new roman",15,"italic bold"),fill="orange")

    root.update_idletasks() # it will show every step of algorithm for e.g comparision between number in bubble sort

#start button for running the  algorithm
def StartAlgorithm():
    global data
    # if not data: return

    if algo_menu.get() == 'Quick Sort':
        quick_sort(data, 0, len(data)-1, drawData, speedscale.get())

    elif algo_menu.get() == 'Bubble Sort':
        bubble_sort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Bucket Sort':
        bucketSort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Merge Sort':
        merge_sort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Insertion Sort':
        insertionSort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Count Sort':
        countingSort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Radix Sort':
        radixSort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Heap Sort':
        heapSort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Hybrid Quick Sort':
        hybrid_quick_sort(data, drawData, speedscale.get())
    elif algo_menu.get() == 'Problem 8.2.4':
        start = simpledialog.askinteger("Input","Starting Point",parent=root)
        end = simpledialog.askinteger("Input","Ending Point",parent=root)
        problem(data, start,end, drawData, speedscale.get())
    # drawData(data, ['green' for x in range(len(data))])
#it will generate the bars according to the number given
def Generate():
    global data
    print('Select Algorithm'+select_algorithm.get())
    # min value
    minivalue=int(minvalue.get())
    maxivalue=int(maxvalue.get())
    sizeevalue=int(sizevalue.get())
    data=[]
    if(algo_menu.get() == 'Bucket Sort'):
		    for _ in range(sizeevalue):
		        data.append(round(random.uniform(0, 1), 2))

    else:
        for _ in range(sizeevalue):
            data.append(random.randrange(minivalue,maxivalue+1))

    drawData(data,["#A90042" for x in range (len(data))])

random_generate = Button(root,text="Generate",bg="#F7A4A4",font=("arial",12,"italic bold"),relief=SUNKEN,
                        activebackground="#05945B",activeforeground="white",bd=5,width=10,command=Generate)
random_generate.place(x=750,y=60)

#Defines the tag of size of array that should be generated
sizevaluelabel=Label(root,text ="Size: ", font=("new roman",12,"italic bold"),bg="#EEF1FF",
                    width=10,fg="black",height=2,relief=GROOVE,bd=5)
sizevaluelabel.place(x=0,y=60)

#Defines the size of array that should be generated
sizevalue= Scale(root,from_=0,to=30,resolution=1,bg="#B1AFFF",orient=HORIZONTAL,font=("arial",14,"italic bold"),
                    relief=GROOVE,bd=2,width=10)
sizevalue.place(x=120,y=60)

#Defines the tag of size of array that should be generated
minvaluelabel=Label(root,text ="Min Value: ", font=("new roman",12,"italic bold"),bg="#EEF1FF",
                    width=10,fg="black",height=2,relief=GROOVE,bd=5)
minvaluelabel.place(x=250,y=60)

#Defines the size of array that should be generated
minvalue= Scale(root,from_=0,to=10,resolution=1,orient=HORIZONTAL,bg="#B1AFFF",font=("arial",14,"italic bold"),
                    relief=GROOVE,bd=2,width=10)
minvalue.place(x=370,y=60)

#Defines the tag of size of array that should be generated
maxvaluelabel=Label(root,text ="Max Value: " ,font=("new roman",12,"italic bold"),bg="#EEF1FF",
                    width=10,fg="black",height=2,relief=GROOVE,bd=5)
maxvaluelabel.place(x=500,y=60)

#Defines the size of array that should be generated
maxvalue= Scale(root,from_=0,to=100,resolution=1,orient=HORIZONTAL,bg="#B1AFFF",font=("arial",14,"italic bold"),
                    relief=GROOVE,bd=2,width=10)
maxvalue.place(x=620,y=60)

# start the algorithm
start= Button(root,text="Start",bg="#BDBDD7",font=("arial",12,"italic bold"),relief=SUNKEN,
                        activebackground="#05945B",activeforeground="white",bd=5,width=10,command=StartAlgorithm)
start.place(x=750,y=0)

#speed of the algorithm
speedlabel=Label(root,text ="Speed: ", font=("new roman",12,"italic bold"),bg="#EEF1FF",
                    width=10,fg="black",height=2,relief=GROOVE,bd=5)
speedlabel.place(x=400,y=0)

speedscale= Scale(root,from_=0.1,to=5.0,resolution=0.2,length=200,digits=2,bg="#B1AFFF",orient=HORIZONTAL,font=("arial",14,"italic bold"),
                    relief=GROOVE,bd=2,width=10)
speedscale.place(x=520,y=0)

graph_label=Button(root,text="Graphical Reprsentation",bg="#F7A4A4",font=("arial",12,"italic bold"),relief=SUNKEN,
                        activebackground="#05945B",activeforeground="white",bd=5,width=20,command=graph)
graph_label.place(x=350,y=120)

#define the box in which whole algorithm will be visualize
canvas=Canvas(root,width=870,height=450,bg="black")
canvas.place(x=10,y=180)

root.mainloop() # main driver which will run whole Gui


from unittest import TestCase
from jsoncfg.config_classes import (
    ValueNotFoundNode, ConfigJSONScalar, ConfigJSONObject, ConfigJSONArray, JSONConfigNodeTypeError, ConfigNode,
    JSONConfigIndexError,
)
from jsoncfg import JSONConfigValueNotFoundError, JSONParserParams, JSONValueMapper
from jsoncfg import (
    loads_config, node_location, node_exists, node_is_object, node_is_array,
    node_is_scalar, ensure_exists, expect_object, expect_array, expect_scalar,
)

from .utils import WrapCallable

TEST_JSON_STRING = """
{
    int: 5,
    float: 1.5,
    str: "strval",
    null: null,
    true: true,
    false: false,
    array: [{a:0},{b:1}],
    obj: {},
}
"""

class TestValueNotFoundNode(TestCase):
    def test_len(self):
        config = loads_config('{}')
        not_found_node = config.woof
        self.assertIsInstance(not_found_node, ValueNotFoundNode)
        self.assertRaises(JSONConfigValueNotFoundError, lambda: len(not_found_node))

    def test_iter(self):
        config = loads_config('{}')
        not_found_node = config.woof
        self.assertIsInstance(not_found_node, ValueNotFoundNode)
        self.assertRaises(JSONConfigValueNotFoundError, lambda: iter(not_found_node))

    def test_indexing(self):
        config = loads_config('{}')
        not_found_node = config.woof
        self.assertIsInstance(not_found_node, ValueNotFoundNode)
        case0 = not_found_node[100][100]
        self.assertRaises(JSONConfigValueNotFoundError, case0)
        case1 = not_found_node['a']['b']
        self.assertRaises(JSONConfigValueNotFoundError, case1)

class TestJSONValueMapper(TestCase):
    def test_call(self):
        self.assertRaises(NotImplementedError, JSONValueMapper(), None)

class TestConfigNode(TestCase):
    def test_value_fetch(self):
        config = loads_config('{a:0, b:{}, c:[]}')
        scalar = config.a
        obj = config.b
        array = config.c
        not_found = config.d

        self.assertIsInstance(scalar, ConfigJSONScalar)
        self.assertIsInstance(obj, ConfigJSONObject)
        self.assertIsInstance(array, ConfigJSONArray)
        self.assertIsInstance(not_found, ValueNotFoundNode)

        default = object()
        self.assertEqual(scalar(default), 0)
        self.assertEqual(obj(default), {})
        self.assertEqual(array(default), [])
        self.assertEqual(not_found(default), default)

    def test_value_fetch_not_json_value_mapper_instance(self):
        config = loads_config('{a:0}')
        self.assertRaisesRegexp(TypeError, r'1 isn\'t a JSONValueMapper instance!',
                                WrapCallable(config.a), 0, 1)

    def test_unimplemented_fetch_unwrapped_value(self):
        self.assertRaises(NotImplementedError, ConfigNode(0, 0)._fetch_unwrapped_value)

class TestConfigJSONScalar(TestCase):
    def test_len(self):
        config = loads_config('[0]', parser_params=JSONParserParams(root_is_array=True))
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to access the __len__ of a scalar config object\.',
            len,
            config[0],
        )

    def test_iter(self):
        config = loads_config('[0]', parser_params=JSONParserParams(root_is_array=True))
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to iterate a scalar value\.',
            iter,
            config[0],
        )

    def test_contains(self):
        config = loads_config('[0]', parser_params=JSONParserParams(root_is_array=True))
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to access the __contains__ magic method of a scalar config object\.',
            config[0].__contains__,
            0,
        )

    def test_getattr(self):
        config = loads_config('[0]', parser_params=JSONParserParams(root_is_array=True))
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to get an item from a scalar as if it was an object\.',
            config[0].__getattr__,
            'woof',
        )

    def test_getitem(self):
        config = loads_config('[0]', parser_params=JSONParserParams(root_is_array=True))
        self.assertRaisesRegexp(
            TypeError,
            r'You are allowed to index only with string or integer\.',
            config[0].__getitem__,
            None,
        )
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to index into a scalar as if it was an array\.',
            config[0].__getitem__,
            0,
        )
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to get an item from a scalar as if it was an object\.',
            config[0].__getitem__,
            'item',
        )

    def test_repr(self):
        config = loads_config('[0]', parser_params=JSONParserParams(root_is_array=True))
        repr(config[0])

class TestConfigJSONObject(TestCase):
    def test_len(self):
        config = loads_config('{k0:0, k1:1}')
        self.assertEqual(len(config), 2)

    def test_iter(self):
        config = loads_config('{k0:0, k1:1}')
        result = {k: v() for k, v in config}
        self.assertDictEqual(result, {'k0': 0, 'k1': 1})

    def test_contains(self):
        config = loads_config('{k0:0}')
        self.assertTrue('k0' in config)
        self.assertFalse('k1' in config)

    def test_getitem(self):
        config = loads_config(TEST_JSON_STRING)
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to index into an object as if it was an array\.',
            config.__getitem__,
            0,
        )
        self.assertRaisesRegexp(
            TypeError,
            r'You are allowed to index only with string or integer\.',
            config.__getitem__,
            None,
        )
        self.assertIsInstance(config.not_found, ValueNotFoundNode)

    def test_item_not_found(self):
        config = loads_config(TEST_JSON_STRING)
        self.assertRaises(JSONConfigValueNotFoundError, config.nonexistent)

    def test_repr(self):
        config = loads_config(TEST_JSON_STRING)
        repr(config)

class TestConfigJSONArray(TestCase):
    @staticmethod
    def loads_array_config(json_string):
        return loads_config(json_string, JSONParserParams(root_is_array=True))

    def test_len(self):
        config = self.loads_array_config('[0, 1]')
        self.assertEqual(len(config), 2)

    def test_iter(self):
        config = self.loads_array_config('[0, 1]')
        items = set(config_val() for config_val in config)
        self.assertSetEqual(items, {0, 1})

    def test_contains(self):
        config = self.loads_array_config('[]')
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to access the __contains__ magic method of an array\.',
            config.__contains__,
            0,
        )

    def test_getattr(self):
        config = loads_config(TEST_JSON_STRING)
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to get an item from an array as if it was an object\.',
            config.array.__getattr__,
            'item',
        )

    def test_getitem(self):
        config = loads_config(TEST_JSON_STRING)
        self.assertEquals(config.array[0].a(), 0)
        self.assertEquals(config.array[1].b(), 1)
        self.assertRaisesRegexp(
            TypeError,
            r'You are allowed to index only with string or integer\.',
            config.array.__getitem__,
            None,
        )
        self.assertRaisesRegexp(
            JSONConfigNodeTypeError,
            r'You are trying to get an item from an array as if it was an object\.',
            config.array.__getitem__,
            'item',
        )

    def test_getitem_with_negative_index(self):
        config = loads_config(TEST_JSON_STRING)
        self.assertDictEqual(config.array[-1](), {'b': 1})
        self.assertDictEqual(config.array[-2](), {'a': 0})
        self.assertRaises(
            JSONConfigIndexError,
            config.array.__getitem__,
            -3,
        )
        self.assertRaises(
            JSONConfigIndexError,
            config.array.__getitem__,
            2,
        )

    def test_repr(self):
        config = loads_config(TEST_JSON_STRING)
        repr(config.array)

class TestUtilityFunctions(TestCase):
    def test_node_location(self):
        config = loads_config('\n{k0:0}')
        location = node_location(config)
        self.assertEquals(location, (2, 1))
        self.assertEquals(location.line, 2)
        self.assertEquals(location.column, 1)

    def test_node_location_with_value_not_found_node(self):
        config = loads_config('{}')
        self.assertRaises(JSONConfigValueNotFoundError, node_location, config.a)

    def test_node_location_with_invalid_value(self):
        self.assertRaisesRegexp(TypeError, r'Expected a config node but received a list instance.',
                                node_location, [])

    def test_node_exists(self):
        config = loads_config('{k0:0}')
        self.assertTrue(node_exists(config))
        self.assertTrue(node_exists(config.k0))
        self.assertFalse(node_exists(config.not_found))

    def test_node_is_object(self):
        config = loads_config('{k0:0}')
        self.assertTrue(node_is_object(config))
        self.assertFalse(node_is_object(config.k0))
        self.assertFalse(node_is_object(config.not_found))

    def test_node_is_array(self):
        config = loads_config('{a:[0]}')
        self.assertTrue(node_is_array(config.a))
        self.assertFalse(node_is_array(config.a[0]))
        self.assertFalse(node_is_array(config.not_found))

    def test_node_is_scalar(self):
        config = loads_config('{a:[0]}')
        self.assertFalse(node_is_scalar(config.a))
        self.assertTrue(node_is_scalar(config.a[0]))
        self.assertFalse(node_is_scalar(config.not_found))

    def test_ensure_exists(self):
        config = loads_config('{}')
        self.assertIs(config, ensure_exists(config))
        self.assertRaises(JSONConfigValueNotFoundError, ensure_exists, config.a)
        self.assertRaises(TypeError, ensure_exists, None)

    def test_expect_object(self):
        config = loads_config('{a:0}')
        self.assertIs(config, expect_object(config))
        self.assertRaises(JSONConfigValueNotFoundError, expect_object, config.b)
        self.assertRaises(JSONConfigNodeTypeError, expect_object, config.a)
        self.assertRaises(TypeError, expect_object, None)

    def test_expect_array(self):
        config = loads_config('{a:[0]}')
        self.assertIs(config.a, expect_array(config.a))
        self.assertRaises(JSONConfigValueNotFoundError, expect_array, config.not_found)
        self.assertRaises(JSONConfigNodeTypeError, expect_array, config.a[0])
        self.assertRaises(TypeError, expect_array, None)

    def test_expect_scalar(self):
        config = loads_config('{a:[0]}')
        self.assertIs(config.a[0], expect_scalar(config.a[0]))
        self.assertRaises(JSONConfigValueNotFoundError, expect_scalar, config.not_found)
        self.assertRaises(JSONConfigNodeTypeError, expect_scalar, config)
        self.assertRaises(TypeError, expect_scalar, None)


import discord
from discord.ext import commands
import time
from datetime import datetime
from datetime import timedelta

startTime = time.time()

class InfoButtons(discord.ui.View):
    def __init__(self, support_server, github):
        super().__init__()
        self.add_item(discord.ui.Button(label="Support Server", url=support_server))
        self.add_item(discord.ui.Button(label="GitHub", url=github))

class Information(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.slash_command(name="help", description="Displays all commands for Picsiv")
    async def help(self, ctx):
        embed = discord.Embed(title="Picsiv Commands", color=0x0096fa,
            description="""
                **picsiv**: Displays basic information about Picsiv.
                **help**: Shows this message.
            """)
        embed.add_field(name="__Art APIs__",
            value="""
                **sugoiart**: Gets a random image from the sugoiart API.
            """, inline=False)
        embed.add_field(name="__Reddit__",
            value="""
                **streetmoe**: Gets a random image from r/streetmoe.
                **animehoodies**: Gets a random image from r/animehoodies.
                **animewallpaper**: Gets a random image from r/animewallpaper.
                **moescape**: Gets a random image from r/moescape.
                **wholesomeyuri**: Gets a random image from r/wholesomeyuri.
                **awwnime**: Gets a random image from r/awwnime.
                **animeirl**: Gets a random image from r/anime_irl.
                **saltsanime**: Gets a random image from r/SaltsAnime.
                **megane**: Gets a random image from r/megane.
                **imaginarymaids**: Gets a random image from r/ImaginaryMaids.
            """, inline=False)
        await ctx.respond(embed=embed)

    @commands.slash_command(name="picsiv", description="Displays basic information about Picsiv")
    async def picsiv(self, ctx):
        activeServers = self.bot.guilds
        botUsers = 0
        for i in activeServers:
            botUsers += i.member_count
        currentTime = time.time()
        differenceUptime = int(round(currentTime - startTime))
        uptime = str(timedelta(seconds = differenceUptime))
        botinfo = discord.Embed(
            title="Picsiv",
            color=0x0096fa,
            timestamp=datetime.now(),
            description=f"Thanks for using Picsiv bot! Any questions can be brought up in the support server. This bot is also open-source! All code can be found on GitHub (Please leave a star тнР if you enjoy the bot).\n\nPrivacy Policy: https://picsiv.hayasaka.moe/privacy\n\n**Server Count:** {len(self.bot.guilds)}\n**Bot Users:** {botUsers}\n**Bot Uptime:** {uptime}"
        )
        botinfo.set_author(name="Picsiv", icon_url=self.bot.user.avatar.url)
        await ctx.respond(embed=botinfo, view=InfoButtons("https://discord.gg/UcYspqftTF", "https://github.com/jckli/picsiv"))

def setup(bot):
    bot.add_cog(Information(bot))


"""

WAP to determine whether a number N is equal to the sum of it's proper positive divisors (excluding the number itslef)
Input:
3 (no of test cases)
6
5
28

Output:
Yes
No
Yes

"""

t = int(input("Enter test cases "))
for j in range(t):
    n = int(input("Enter num "))

    sum = 0
    for i in range(1, n):
        if n % i == 0:
            sum += i
    if sum == n:
        print("Yes")
    else:
        print("No")


def main():
    """
        Retrieves services starts check_service in a gevent pool of 100.
    """
    search = ServiceSearch()
    services = search.get_services(up=True, tags=['!header_scan'])
    print_notification("Scanning {} services".format(len(services)))

    # Disable the insecure request warning
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

    pool = Pool(100)
    count = 0
    for service in services:
        count += 1
        if count % 50 == 0:
            print_notification("Checking {}/{} services".format(count, len(services)))
        pool.spawn(check_service, service)

    pool.join()
    print_notification("Completed, 'http' tag added to services that respond to http, 'https' tag added to services that respond to https.")


from wpilib.command.commandgroup import CommandGroup
import commandbased.flowcontrol as fc

import robot

from commands.hatch.hatchintakecommand import HatchIntakeCommand
from commands.hatch.hatchejectcommand import HatchEjectCommand
from commands.hatch.defaultcommand import DefaultCommand

from commands.lights.seizurelightscommand import SeizureLightsCommand
#from commands.lights.solidredcommand import SolidRedCommand

from commands.drivetrain.gototapecommand import GoToTapeCommand
from commands.drivetrain.tapepidcommand import TapePIDCommand
from commands.drivetrain.tapepidhighcommand import tapePIDHighCommand
from commands.drivetrain.tapepidlowcommand import TapePIDLowCommand
from commands.drivetrain.gopasttapecommand import GoPastTapeCommand
from commands.drivetrain.transitionmovecommand import TransitionMoveCommand
#from commands.drivetrain.limelightoffcommand import limeLightOffCommand
from commands.resetcommand import ResetCommand

class TapePIDCommandGroup(CommandGroup):

    def __init__(self, pipeline=1):
        super().__init__('Tape P I D')

        # Add commands here with self.addSequential() and self.addParallel()
        @fc.IF(lambda: not robot.hatch.hasHatchPanel())
        def grabHatch(self):
            self.addParallel(HatchIntakeCommand())
            self.addSequential(GoToTapeCommand(pipeline))
            self.addParallel(HatchIntakeCommand(True), 2)
            self.addSequential(GoPastTapeCommand(), 0.25)
            self.addParallel(SeizureLightsCommand(), 3)
            self.addSequential(TransitionMoveCommand(-100,-100,-12,-12,0,0))
            self.addSequential(ResetCommand())

        @fc.ELIF(lambda: robot.hatch.hasHatchPanel() and robot.elevator.isLow())
        def firstLevel(self):
            self.addSequential(TapePIDLowCommand())
            self.addSequential(GoPastTapeCommand(), 0.25)
            self.addParallel(HatchEjectCommand())
            self.addParallel(SeizureLightsCommand(), 3)
            self.addSequential(TransitionMoveCommand(-100,-100,-12,-12,0,0))
            self.addSequential(ResetCommand())
        @fc.ELIF(lambda: robot.hatch.hasHatchPanel() and robot.elevator.isMid())
        def secondLevel(self):
            self.addSequential(TapePIDCommand())
            self.addSequential(GoPastTapeCommand(), 0.25)
            self.addParallel(HatchEjectCommand())
            self.addParallel(SeizureLightsCommand(), 3)
            self.addSequential(TransitionMoveCommand(-100,-100,-12,-12,0,0))
            self.addSequential(ResetCommand())
        @fc.ELIF(lambda: robot.hatch.hasHatchPanel() and robot.elevator.isHigh())
        def thridLevel(self):
            self.addSequential(tapePIDHighCommand())
            self.addSequential(GoPastTapeCommand(), 0.25)
            self.addParallel(HatchEjectCommand())
            self.addParallel(SeizureLightsCommand(), 3)
            self.addSequential(TransitionMoveCommand(-100,-100,-12,-12,0,0))
            self.addSequential(ResetCommand())


import data2pcd.converter as json2pcd
import click
import os
from pathlib import Path

@click.command()
@click.option('--json', help="The path of input JSON file")
@click.option('--depth', default=True, type=bool, help="If plot depth map")
@click.option('--image', default=True, type=bool, help="If plot rgb image")
@click.option('--confidence', default=False, type=bool, help="If plot confidence map")
@click.option('--mask', default=False, type=bool, help="If plot mask")
def plot(json, depth, image, confidence, mask):
    """Plot JSON from command line"""
    json_converter = json2pcd.Converter()
    json_path = Path(json)
    if json_path.is_dir():
        json_files = sorted(json_path.glob("*.json"), key=os.path.getmtime)
        for json_file in json_files:
            print(f"Plot json file: {json_file}")
            json_converter.load_json(Path(json_file))
            json_converter.visualize(name=str(json_file.stem),
                                     depth=depth,
                                     image=image,
                                     confidence=confidence,
                                     mask=mask)
    else:
        json_converter.load_json(json_path)
        json_converter.visualize(name=str(json_path.stem),
                                 depth=depth,
                                 image=image,
                                 confidence=confidence,
                                 mask=mask)

if __name__ == "__main__":
    # execute only if run as a script
    plot()


import sys
import os
from PIL import Image

# Gets the name of the root new root diectory.
def get_root_name():
    root = input("What folder are you trying to access? ")
    return root

# Gets the new folder name from the user.
def get_newfolder_name():
    new_folder_name = input("Name of folder converted files will be stored. ")
    return new_folder_name

# Returns true if a file is not in JPEG format
# and false if it is not.
def is_jpg(filename):
    try:
        image = Image.open(filename)
        return image.format =='JPEG'
    except IOError:
        return False

# Creates a new  directory with the  folder name
# and current root name
def create_directory(current_path):
    if not os.path.exists(current_path):
        os.makedirs(f'./{root}/{new_folder_name}')

# Saves converted  files into the new directory.
def save_files(entries):
    for entry in entries:
        file_name = f'./{root}/{entry.name}'
        if is_jpg(file_name):

            name = str(entry.name)

            edited_name = name.replace('.jpg','.png')

            img = Image.open(file_name)

            new_name = f'./{root}/{new_folder_name}/{edited_name}'

            img.save(new_name,'png')

#Gets the main(root) directory name from the user.
root = get_root_name()

#Gets the new folder name from the user.
new_folder_name = get_newfolder_name()

current_path = f'./{root}/{new_folder_name}'

# Scans all files in a directory
# and stores them in entries

with os.scandir(f'./{root}') as entries:

    # Creates a new directory based on the current path
    # and the directory name given.
    create_directory(current_path)

    # Saves all the converted files in
    # the directory that was created
    save_files(entries)


from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Board
from .forms import BoardForm
from django.shortcuts import redirect
from datetime import datetime

def home(request):
    return render(request, 'board/index.html', {})

def board_list(request):
    posts = Board.objects.filter(created_date__lte=timezone.now()).order_by('created_date')
    return render(request, 'board/post_list.html', {'posts': posts})

def post_detail(request, pk):
    posts = Board.objects.filter(created_date__lte=timezone.now()).order_by('created_date')
    post = get_object_or_404(Board, pk=pk)
    return render(request, 'board/post_detail.html', {'post':post, 'posts':posts})

def post_new(request):
    if request.method == "POST":
        form = BoardForm(request.POST)
        if form.is_valid():
            current = datetime.today()
            post = form.save(commit=False)
            post.author = request.user
            post.created_date = current.strftime('%Y-%m-%d')
            post.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = BoardForm()
    return render(request, 'board/post_edit.html', {'form': form})

def post_edit(request, pk):
    post = get_object_or_404(Board, pk=pk)
    if request.method == "POST":
        form = BoardForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.created_date = timezone.now()
            post.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = BoardForm(instance=post)
    return render(request, 'board/post_edit.html', {'form': form})

def post_delete(request, pk):
    post = get_object_or_404(Board, pk=pk)
    post.delete()
    return redirect('board_list')

# Create your views here.


with open('20_day_input.txt') as data_doc:
	data = int(data_doc.read())

print(data)

elfs = []

def elf_generator(n):
	elf = []
	elf.append(n)
	elf.append(n * 10)
	return elf

# for i in range(1, 11):
# 	elfs.append(elf_generator(i))
# 	presents = 0
# 	for j in elfs:
# 		if i % j[0] == 0:
# 			presents += j[1]
# 	print(i)
# 	print(presents)
# 	print()

# print(elfs)

i = 1
while True:
	elfs.append(elf_generator(i))
	presents = 0
	for j in elfs:
		if i % j[0] == 0:
			presents += j[1]
	print(presents)
	if presents >= data:
		print(i)
		break
	else:
		i += 1


import cv2
import numpy as np
import matplotlib.pyplot as plt
import random

class pixel:
    def __init__(self, y = 0, x = 0):
        self.y = y
        self.x = x

image = cv2.imread('hw1_sample_images/sample3.jpg', 0)
avg = len(image)*len(image[0])/256

hist = {}
for i in range(256):
    hist[i] = []

for y in range(len(image)):
    for x in range(len(image[0])):
        hist[image[y][x]].append(pixel(y,x))

output = image.copy()
pointer = 0
for i in range(256):
    while len(hist[i]) < avg :
        if len(hist[pointer]) <=avg or pointer == i:
            pointer +=1
        change_p = hist[pointer].pop(random.randrange(len(hist[pointer])))
        #change_p = hist[pointer].pop(1)
        output[change_p.y][change_p.x] = i
        hist[i].append(change_p)
cv2.imwrite('5_result.jpg', output)

plt.hist(output.ravel(), 256, [0,256])
plt.show()

2021S - Digital Image Processing/hw1/problem2_hist_equ.py


import socket
sock = socket.socket()
sock.connect(('127.0.0.1',9090))

def exec(command):
    if not command:
        exec(command)
    elif command == "exit":
        quit()
    else:
        sock.send(command.encode())
while True:
    command = input("Type Command: ")
    exec(command)
    data = sock.recv(1024).decode()
    print("last command: " + data)


#!/usr/bin/env python

import rospy
import math
import gps_util
from navigation_msgs.msg import WaypointsArray
from sensor_msgs.msg import NavSatFix

class WaypointHandler(object):
    ''' must supply a minimum tolerance -- the distance threshold between
    current location and target where the cart is considered to have
    reached the target waypoint. '''
    def __init__(self, min_tolerance):
        self.goal_index = None #index of the current target in waypoints array
        self.waypoints = None #waypoints are of type NavSatFix
        self.curr_pos = None
        self.min_tolerance = min_tolerance
        self.tolerance = min_tolerance

    def gps_array_to_xyz_array(self, gps_array):
        return gps_util.add_intermediate_points(
            [
             gps_util.lat_long_to_xyz(gps.latitude, gps.longitude) for gps in gps_array
            ],
            1
        )
    ''' called when we are updated with a new list of points (the goal has been changed).
    The old waypoints are forgotten/ignored completely. '''
    def set_points(self, array):
        self.waypoints = self.gps_array_to_xyz_array(array);
        self.goal_index = 0
        self.curr_pos = None

    ''' increments the current point index and returns false if there are no more indices.
    should be called when the previous goal has been reached '''
    def next_point(self):
        if self.goal_index >= self.waypoints.length()-1:
            return False
        else:
            goal_index += 1
            return True

    ''' rough estimate of distance from goal (assumes perfect turning)
    adds up the distance between the current position and the first waypoint
    and between each waypoint not yet reached'''
    def distance_from_end(self):
        distance = 0
        if not self.waypoints:
            return distance
        distance += self.distance_from_next()
        for i in range(self.goal_index, waypoints.length()-1):
            point = self.waypoints[i]
            point2 = self.waypoints[i+1]
            distance += gps_util.xyz_distance_between_points(point, point2)
        return distance

    ''' indicates whether the next point has been reached '''
    def reached_next_point(self):
        if not self.waypoints:
            return False
        return math.abs(self.distance_from_next()) < self.tolerance
    ''' angle between car and the next point in degrees '''
    def angle_from_next(self):
        goal = self.get_goal()
        if not goal:
            return None
        return gps_util.xy_angle_between_points(self.curr_pos.pose.pose.position, goal)

    ''' distance between car and the next point '''
    def distance_from_next(self):
        goal = self.get_goal()
        if not goal:
            return None
        return gps_util.xyz_dist_between_points(self.curr_pos.pose.pose.position, goal)

    ''' current position is a message of type nav_msgs/Odometry.
    Returns true if the final goal point has been reached'''
    def update_pos(self, current_position, tolerance):
        if not current_position:
            return False
        self.curr_pos = current_position
        self.tolerance = self.min_tolerance if (self.min_tolerance > tolerance) else tolerance
        return self.reached_next_point() and not self.next_point()
        #this line will call the necessary methods -- only return true when the final goal has been reached

    ''' retrieves the current goal '''
    def get_goal(self):
        point = self.get_point_at_index(self.goal_index)
        if not point:
            return None
        return point

    def get_point_at_index(self, index):
        if not self.waypoints or index >= self.waypoints.length():
            return None
        goal = self.waypoints[index]
        xyz_goal = gps_util.lat_long_to_xyz(goal.latitude, goal.longitude)
        goal_point = Point()
        goal_point.x = xyz_goal[0] #x in tuple
        goal_point.y = xyz_goal[1] #y in tuple
        goal_point.z = 0 #elevation is always 0 for now
        return goal_point

if __name__ == '__main__':
    try:
       WaypointHandler()
    except rospy.ROSInterruptException:
        pass


import numpy as np

from tractor import mixture_profiles as mp
from tractor.utils import ParamList, MultiParams, ScalarParam, BaseParams
from tractor.patch import Patch, add_patches, ModelMask
from tractor.basics import SingleProfileSource, BasicSource

debug_ps = None

def get_galaxy_cache():
    return None

def set_galaxy_cache_size(N=10000):
    # Ugh, dealing with caching + extents / modelMasks was too much
    pass

enable_galaxy_cache = set_galaxy_cache_size

def disable_galaxy_cache():
    pass

class GalaxyShape(ParamList):
    '''
    A naive representation of an ellipse (describing a galaxy shape),
    using effective radius (in arcsec), axis ratio, and position angle.

    For better ellipse parameterizations, see ellipses.py
    '''
    @staticmethod
    def getName():
        return "Galaxy Shape"

    @staticmethod
    def getNamedParams():
        '''
        re: arcsec
        ab: axis ratio, dimensionless, in [0,1]
        phi: deg, "E of N", 0=direction of increasing Dec,
        90=direction of increasing RA
        '''
        return dict(re=0, ab=1, phi=2)

    def __repr__(self):
        return 're=%g, ab=%g, phi=%g' % (self.re, self.ab, self.phi)

    def __str__(self):
        return ('%s: re=%.2f, ab=%.2f, phi=%.1f' %
                (self.getName(), self.re, self.ab, self.phi))

    def getTensor(self, cd):
        # G takes unit vectors (in r_e) to degrees (~intermediate world coords)
        G = self.getRaDecBasis()
        # "cd" takes pixels to degrees (intermediate world coords)
        # T takes pixels to unit vectors.
        T = np.dot(np.linalg.inv(G), cd)
        return T

    def getRaDecBasis(self):
        '''
        Returns a transformation matrix that takes vectors in r_e
        to delta-RA, delta-Dec vectors.
        '''
        # # convert re, ab, phi into a transformation matrix
        phi = np.deg2rad(90 - self.phi)
        # # convert re to degrees
        # # HACK -- bring up to a minimum size to prevent singular
        # # matrix inversions
        re_deg = max(1. / 30, self.re) / 3600.
        cp = np.cos(phi)
        sp = np.sin(phi)
        # Squish, rotate, and scale into degrees.
        # resulting G takes unit vectors (in r_e) to degrees
        # (~intermediate world coords)
        return re_deg * np.array([[cp, sp * self.ab], [-sp, cp * self.ab]])

class Galaxy(MultiParams, SingleProfileSource):
    '''
    Generic Galaxy profile with position, brightness, and shape.
    '''

    def __init__(self, *args):
        super(Galaxy, self).__init__(*args)
        self.name = self.getName()
        self.dname = self.getDName()

    @staticmethod
    def getNamedParams():
        return dict(pos=0, brightness=1, shape=2)

    def getName(self):
        return 'Galaxy'

    def getDName(self):
        '''
        Name used in labeling the derivative images d(Dname)/dx, eg
        '''
        return 'gal'

    def getSourceType(self):
        return self.name

    def getShape(self):
        return self.shape

    def __str__(self):
        return (self.name + ' at ' + str(self.pos)
                + ' with ' + str(self.brightness)
                + ' and ' + str(self.shape))

    def __repr__(self):
        return (self.name + '(pos=' + repr(self.pos) +
                ', brightness=' + repr(self.brightness) +
                ', shape=' + repr(self.shape) + ')')

    def getUnitFluxModelPatch(self, img, **kwargs):
        raise RuntimeError('getUnitFluxModelPatch unimplemented in' +
                           self.getName())

    # returns [ Patch, Patch, ... ] of length numberOfParams().
    # Galaxy.
    def getParamDerivatives(self, img, modelMask=None, **kwargs):
        pos0 = self.getPosition()
        (px0, py0) = img.getWcs().positionToPixel(pos0, self)
        counts = img.getPhotoCal().brightnessToCounts(self.brightness)

        minsb = img.modelMinval
        if counts > 0:
            minval = minsb / counts
        else:
            minval = None

        patch0 = self.getUnitFluxModelPatch(img, px=px0, py=py0, minval=minval,
                                            modelMask=modelMask, **kwargs)
        if patch0 is None:
            return [None] * self.numberOfParams()
        derivs = []

        if modelMask is None:
            modelMask = ModelMask.fromExtent(*patch0.getExtent())
        assert(modelMask is not None)

        # FIXME -- would we be better to do central differences in
        # pixel space, and convert to Position via CD matrix?

        # derivatives wrt position
        psteps = pos0.getStepSizes()
        if not self.isParamFrozen('pos'):
            params = pos0.getParams()
            if counts == 0:
                derivs.extend([None] * len(params))
                psteps = []
            for i, pstep in enumerate(psteps):
                oldval = pos0.setParam(i, params[i] + pstep)
                (px, py) = img.getWcs().positionToPixel(pos0, self)
                pos0.setParam(i, oldval)
                patchx = self.getUnitFluxModelPatch(
                    img, px=px, py=py, minval=minval, modelMask=modelMask,
                    **kwargs)
                if patchx is None or patchx.getImage() is None:
                    derivs.append(None)
                    continue
                dx = (patchx - patch0) * (counts / pstep)
                dx.setName('d(%s)/d(pos%i)' % (self.dname, i))
                derivs.append(dx)

        # derivatives wrt brightness
        bsteps = self.brightness.getStepSizes()
        if not self.isParamFrozen('brightness'):
            params = self.brightness.getParams()
            for i, bstep in enumerate(bsteps):
                oldval = self.brightness.setParam(i, params[i] + bstep)
                countsi = img.getPhotoCal().brightnessToCounts(self.brightness)
                self.brightness.setParam(i, oldval)
                df = patch0 * ((countsi - counts) / bstep)
                df.setName('d(%s)/d(bright%i)' % (self.dname, i))
                derivs.append(df)

        # derivatives wrt shape
        gsteps = self.shape.getStepSizes()
        if not self.isParamFrozen('shape'):
            gnames = self.shape.getParamNames()
            oldvals = self.shape.getParams()
            if counts == 0:
                derivs.extend([None] * len(oldvals))
                gsteps = []
            for i, gstep in enumerate(gsteps):
                oldval = self.shape.setParam(i, oldvals[i] + gstep)
                patchx = self.getUnitFluxModelPatch(
                    img, px=px0, py=py0, minval=minval, modelMask=modelMask,
                    **kwargs)
                self.shape.setParam(i, oldval)
                if patchx is None:
                    print('patchx is None:')
                    print('  ', self)
                    print('  stepping galaxy shape',
                          self.shape.getParamNames()[i])
                    print('  stepped', gsteps[i])
                    print('  to', self.shape.getParams()[i])
                    derivs.append(None)
                    continue
                dx = (patchx - patch0) * (counts / gstep)
                dx.setName('d(%s)/d(%s)' % (self.dname, gnames[i]))
                derivs.append(dx)
        return derivs

class ProfileGalaxy(object):
    '''
    A mix-in class that renders itself based on a Mixture-of-Gaussians
    profile.
    '''

    def getName(self):
        return 'ProfileGalaxy'

    def getProfile(self):
        return None

    # Here's the main method to override;
    def _getAffineProfile(self, img, px, py):
        ''' Returns a MixtureOfGaussians profile that has been
        affine-transformed into the pixel space of the image.
        '''
        return None

    def _getUnitFluxDeps(self, img, px, py):
        return None

    def _getUnitFluxPatchSize(self, img, **kwargs):
        return 0

    def getUnitFluxModelPatch(self, img, px=None, py=None, minval=0.0,
                              modelMask=None, **kwargs):
        if px is None or py is None:
            (px, py) = img.getWcs().positionToPixel(self.getPosition(), self)
        patch = self._realGetUnitFluxModelPatch(
            img, px, py, minval, modelMask=modelMask, **kwargs)
        if patch is not None and modelMask is not None:
            assert(patch.shape == modelMask.shape)
        return patch

    def _realGetUnitFluxModelPatch(self, img, px, py, minval, modelMask=None,
                                   inner_real_nsigma = 3.,
                                   outer_real_nsigma = 4.,
                                   force_halfsize=None,
                                   **kwargs):
        from astrometry.util.miscutils import get_overlapping_region
        if modelMask is not None:
            x0, y0 = modelMask.x0, modelMask.y0
        else:
            # choose the patch size
            halfsize = self._getUnitFluxPatchSize(img, px=px, py=py, minval=minval)
            # find overlapping pixels to render
            (outx, inx) = get_overlapping_region(
                int(np.floor(px - halfsize)), int(np.ceil(px + halfsize + 1)),
                0, img.getWidth())
            (outy, iny) = get_overlapping_region(
                int(np.floor(py - halfsize)), int(np.ceil(py + halfsize + 1)),
                0, img.getHeight())
            if inx == [] or iny == []:
                # no overlap
                return None
            x0, x1 = outx.start, outx.stop
            y0, y1 = outy.start, outy.stop

        psf = img.getPsf()

        # We have two methods of rendering profile galaxies: If the
        # PSF can be represented as a mixture of Gaussians, then we do
        # the analytic Gaussian convolution, producing a larger
        # mixture of Gaussians, and we render that.  Otherwise
        # (pixelized PSFs), we FFT the PSF, multiply by the analytic
        # FFT of the galaxy, and IFFT back to get the rendered
        # profile.

        # The "HybridPSF" class is just a marker to indicate whether this
        # code should treat the PSF as a hybrid.
        from tractor.psf import HybridPSF
        hybrid = isinstance(psf, HybridPSF)

        def run_mog(amix=None, mm=None):
            ''' This runs the mixture-of-Gaussians convolution method.
            '''
            if amix is None:
                amix = self._getAffineProfile(img, px, py)
            #print('Evaluating MoG mixture:', len(amix.amp))
            #print('amps:', amix.amp)
            if mm is None:
                mm = modelMask
            # now convolve with the PSF, analytically
            # (note that the psf's center is *not* set to px,py; that's just
            #  the position to use for spatially-varying PSFs)
            psfmix = psf.getMixtureOfGaussians(px=px, py=py)
            cmix = amix.convolve(psfmix)
            if mm is None:
                #print('Mixture to patch: amix', amix, 'psfmix', psfmix, 'cmix', cmix)
                return mp.mixture_to_patch(cmix, x0, x1, y0, y1, minval)
            # The convolved mixture *already* has the px,py offset added
            # (via px,py to amix) so set px,py=0,0 in this call.
            if mm.mask is not None:
                p = cmix.evaluate_grid_masked(mm.x0, mm.y0, mm.mask, 0., 0.)
            else:
                p = cmix.evaluate_grid(mm.x0, mm.x1, mm.y0, mm.y1, 0., 0.)
            assert(p.shape == mm.shape)
            return p

        if hasattr(psf, 'getMixtureOfGaussians') and not hybrid:
            return run_mog(mm=modelMask)

        # Otherwise, FFT:
        imh, imw = img.shape
        if modelMask is None:
            # Avoid huge galaxies -> huge halfsize in a tiny image (blob)
            imsz = max(imh, imw)
            halfsize = min(halfsize, imsz)
            # FIXME -- should take some kind of combination of
            # modelMask, PSF, and Galaxy sizes!

        else:
            # ModelMask sets the sizes.
            mh, mw = modelMask.shape
            x1 = x0 + mw
            y1 = y0 + mh

            halfsize = max(mh / 2., mw / 2.)
            # How far from the source center to furthest modelMask edge?
            # FIXME -- add 1 for Lanczos margin?
            halfsize = max(halfsize, max(max(1 + px - x0, 1 + x1 - px),
                                         max(1 + py - y0, 1 + y1 - py)))
            psfh, psfw = psf.shape
            halfsize = max(halfsize, max(psfw / 2., psfh / 2.))
            #print('Halfsize:', halfsize)
            if force_halfsize is not None:
                halfsize = force_halfsize
            # is the source center outside the modelMask?
            sourceOut = (px < x0 or px > x1 - 1 or py < y0 or py > y1 - 1)
            # print('mh,mw', mh,mw, 'sourceout?', sourceOut)

            if sourceOut:
                if hybrid:
                    return run_mog(mm=modelMask)

                # Super Yuck -- FFT, modelMask, source is outside the
                # box.
                neardx, neardy = 0., 0.
                if px < x0:
                    neardx = x0 - px
                if px > x1:
                    neardx = px - x1
                if py < y0:
                    neardy = y0 - py
                if py > y1:
                    neardy = py - y1
                nearest = np.hypot(neardx, neardy)
                #print('Nearest corner:', nearest, 'vs radius', self.getRadius())
                if nearest > self.getRadius():
                    return None
                # how far is the furthest point from the source center?
                farw = max(abs(x0 - px), abs(x1 - px))
                farh = max(abs(y0 - py), abs(y1 - py))
                bigx0 = int(np.floor(px - farw))
                bigx1 = int(np.ceil(px + farw))
                bigy0 = int(np.floor(py - farh))
                bigy1 = int(np.ceil(py + farh))
                bigw = 1 + bigx1 - bigx0
                bigh = 1 + bigy1 - bigy0
                boffx = x0 - bigx0
                boffy = y0 - bigy0
                assert(bigw >= mw)
                assert(bigh >= mh)
                assert(boffx >= 0)
                assert(boffy >= 0)
                bigMask = np.zeros((bigh, bigw), bool)
                if modelMask.mask is not None:
                    bigMask[boffy:boffy + mh,
                            boffx:boffx + mw] = modelMask.mask
                else:
                    bigMask[boffy:boffy + mh, boffx:boffx + mw] = True
                bigMask = ModelMask(bigx0, bigy0, bigMask)
                # print('Recursing:', self, ':', (mh,mw), 'to', (bigh,bigw))
                bigmodel = self._realGetUnitFluxModelPatch(
                    img, px, py, minval, modelMask=bigMask)
                return Patch(x0, y0,
                             bigmodel.patch[boffy:boffy + mh, boffx:boffx + mw])

        # print('Getting Fourier transform of PSF at', px,py)
        # print('Tim shape:', img.shape)
        P, (cx, cy), (pH, pW), (v, w) = psf.getFourierTransform(px, py, halfsize)

        dx = px - cx
        dy = py - cy
        if modelMask is not None:
            # the Patch we return *must* have this origin.
            ix0 = x0
            iy0 = y0
            # the difference that we have to handle by shifting the model image
            mux = dx - ix0
            muy = dy - iy0
            # we will handle the integer portion by computing a shifted image
            # and copying it into the result
            sx = int(np.round(mux))
            sy = int(np.round(muy))
            # the subpixel portion will be handled with a Lanczos interpolation
            mux -= sx
            muy -= sy
        else:
            # Put the integer portion of the offset into Patch x0,y0
            ix0 = int(np.round(dx))
            iy0 = int(np.round(dy))
            # the subpixel portion will be handled with a Lanczos interpolation
            mux = dx - ix0
            muy = dy - iy0

        # At this point, mux,muy are both in [-0.5, 0.5]
        assert(np.abs(mux) <= 0.5)
        assert(np.abs(muy) <= 0.5)

        amix = self._getShearedProfile(img, px, py)
        fftmix = amix
        mogmix = None

        if hybrid and inner_real_nsigma is not None and outer_real_nsigma is not None:
            # Split "amix" into terms that we will evaluate using MoG
            # vs FFT.
            vv = amix.var[:, 0, 0] + amix.var[:, 1, 1]
            # Ramp between:
            nsigma1 = inner_real_nsigma
            nsigma2 = outer_real_nsigma
            # Terms that will wrap-around significantly if evaluated
            # with FFT...  We want to know: at the outer edge of this
            # patch, how many sigmas out are we?  If small (ie, the
            # edge still has a significant fraction of the flux),
            # render w/ MoG.
            IM = ((pW/2)**2 < (nsigma2**2 * vv))
            IF = ((pW/2)**2 > (nsigma1**2 * vv))
            #print('Evaluating', np.sum(IM), 'terms as MoG,', np.sum(IF), 'with FFT,',
            #      np.sum(IM) + np.sum(IF) - len(IM), 'with both')
            #print('  sizes vs PSF size', pW, ':', ', '.join(['%.3g' % s for s in np.sqrt(vv)]))
            ramp = np.any(IM*IF)

            if np.any(IM):
                amps = amix.amp[IM]
                if ramp:
                    ns = (pW/2) / np.maximum(1e-6, np.sqrt(vv))
                    mogweights = np.minimum(1., (nsigma2 - ns[IM]) / (nsigma2 - nsigma1))
                    fftweights = np.minimum(1., (ns[IF] - nsigma1) / (nsigma2 - nsigma1))
                    assert(np.all(mogweights > 0.))
                    assert(np.all(mogweights <= 1.))
                    assert(np.all(fftweights > 0.))
                    assert(np.all(fftweights <= 1.))
                    amps *= mogweights
                mogmix = mp.MixtureOfGaussians(amps,
                                               amix.mean[IM, :] + np.array([px, py])[np.newaxis, :],
                                               amix.var[IM, :, :], quick=True)

            if np.any(IF):
                amps = amix.amp[IF]
                if ramp:
                    amps *= fftweights
                fftmix = mp.MixtureOfGaussians(amps, amix.mean[IF, :], amix.var[IF, :, :],
                                                quick=True)
            else:
                fftmix = None

        if fftmix is not None:
            #print('Evaluating FFT mixture:', len(fftmix.amp), 'components in size', pH,pW)
            #print('Amps:', fftmix.amp)
            Fsum = fftmix.getFourierTransform(v, w, zero_mean=True)
            # In Intel's mkl_fft library, the irfftn code path is faster than irfft2
            # (the irfft2 version sets args (to their default values) which triggers padding
            #  behavior, changing the FFT size and copying behavior)
            #G = np.fft.irfft2(Fsum * P, s=(pH, pW))
            G = np.fft.irfftn(Fsum * P)

            assert(G.shape == (pH,pW))
            # FIXME -- we could try to be sneaky and Lanczos-interp
            # after cutting G down to nearly its final size... tricky
            # tho

            # Lanczos-3 interpolation in ~the same way we do for
            # pixelized PSFs.
            from tractor.psf import lanczos_shift_image
            G = G.astype(np.float32)
            if mux != 0.0 or muy != 0.0:
                lanczos_shift_image(G, mux, muy, inplace=True)
        else:
            G = np.zeros((pH, pW), np.float32)

        if modelMask is not None:
            gh, gw = G.shape
            if sx != 0 or sy != 0:
                yi, yo = get_overlapping_region(-sy, -sy + mh - 1, 0, gh - 1)
                xi, xo = get_overlapping_region(-sx, -sx + mw - 1, 0, gw - 1)
                # shifted
                # FIXME -- are yo,xo always the whole image?  If so, optimize
                shG = np.zeros((mh, mw), G.dtype)
                shG[yo, xo] = G[yi, xi]

                if debug_ps is not None:
                    _fourier_galaxy_debug_plots(G, shG, xi, yi, xo, yo, P, Fsum,
                                                pW, pH, psf)

                G = shG
            if gh > mh or gw > mw:
                G = G[:mh, :mw]
            assert(G.shape == modelMask.shape)

        else:
            # Clip down to suggested "halfsize"
            if x0 > ix0:
                G = G[:, x0 - ix0:]
                ix0 = x0
            if y0 > iy0:
                G = G[y0 - iy0:, :]
                iy0 = y0
            gh, gw = G.shape
            if gw + ix0 > x1:
                G = G[:, :x1 - ix0]
            if gh + iy0 > y1:
                G = G[:y1 - iy0, :]

        if mogmix is not None:
            if modelMask is not None:
                mogpatch = run_mog(amix=mogmix, mm=modelMask)
            else:
                gh, gw = G.shape
                mogpatch = run_mog(amix=mogmix, mm=ModelMask(ix0, iy0, gw, gh))
            assert(mogpatch.patch.shape == G.shape)
            G += mogpatch.patch

        return Patch(ix0, iy0, G)

def _fourier_galaxy_debug_plots(G, shG, xi, yi, xo, yo, P, Fsum,
                                pW, pH, psf):
    import pylab as plt
    mx = G.max()
    ima = dict(vmin=np.log10(mx) - 6,
               vmax=np.log10(mx),
               interpolation='nearest', origin='lower')
    plt.clf()
    plt.subplot(1, 2, 1)
    #plt.imshow(shG, interpolation='nearest', origin='lower')
    plt.imshow(np.log10(shG), **ima)
    ax = plt.axis()
    plt.plot([xo.start, xo.start, xo.stop - 1, xo.stop - 1, xo.start],
             [yo.start, yo.stop - 1, yo.stop - 1, yo.start, yo.start],
             'r-')
    plt.axis(ax)
    plt.title('shG')
    plt.subplot(1, 2, 2)
    #plt.imshow(G, interpolation='nearest', origin='lower')
    plt.imshow(np.log10(G), **ima)
    ax = plt.axis()
    plt.plot([xi.start, xi.start, xi.stop - 1, xi.stop - 1, xi.start],
             [yi.start, yi.stop - 1, yi.stop - 1, yi.start, yi.start],
             'r-')
    plt.axis(ax)
    plt.title('G')
    debug_ps.savefig()

    def plot_real_imag(F, name):
        plt.clf()
        plt.subplot(2, 2, 1)
        print(name, 'real range', F.real.min(), F.real.max())
        plt.imshow(np.log10(np.abs(F.real)),
                   interpolation='nearest', origin='lower')
        plt.xticks([])
        plt.yticks([])
        plt.colorbar()
        plt.title('log(abs(%s.real))' % name)
        plt.subplot(2, 2, 3)
        plt.imshow(np.sign(F.real),
                   vmin=-1, vmax=1,
                   interpolation='nearest', origin='lower', cmap='RdBu')
        plt.xticks([])
        plt.yticks([])
        plt.title('sign(%s.real)' % name)
        print(name, 'imag range', F.imag.min(), F.imag.max())
        plt.subplot(2, 2, 2)
        mx = np.abs(F.imag).max()
        plt.imshow(np.log10(np.abs(F.imag)),
                   interpolation='nearest', origin='lower')
        plt.xticks([])
        plt.yticks([])
        plt.colorbar()
        plt.title('log(abs(%s.imag))' % name)
        plt.subplot(2, 2, 4)
        plt.imshow(np.sign(F.imag),
                   vmin=-1, vmax=1,
                   interpolation='nearest', origin='lower', cmap='RdBu')
        plt.xticks([])
        plt.yticks([])
        plt.title('sign(%s.imag)' % name)

    plot_real_imag(P, 'PSF')
    debug_ps.savefig()
    plot_real_imag(Fsum, 'FFT(Galaxy)')
    debug_ps.savefig()
    plot_real_imag(P * Fsum, 'FFT(PSF * Galaxy)')
    debug_ps.savefig()

    plt.clf()
    p = np.fft.irfft2(P, s=(pH, pW))
    ax = plt.axis([pW // 2 - 7, pW // 2 + 7, pH // 2 - 7, pH // 2 + 7])
    plt.subplot(1, 3, 1)
    plt.imshow(p, interpolation='nearest', origin='lower')
    plt.axis(ax)
    plt.title('psf (real space)')
    # This is in the corners...
    g = np.fft.irfft2(Fsum, s=(pH, pW))
    plt.subplot(1, 3, 2)
    plt.imshow(g, interpolation='nearest', origin='lower')
    plt.title('galaxy (real space)')
    c = np.fft.irfft2(Fsum * P, s=(pH, pW))
    plt.subplot(1, 3, 3)
    plt.imshow(c, interpolation='nearest', origin='lower')
    plt.axis(ax)
    plt.title('convolved (real space)')
    debug_ps.savefig()

    # # What kind of artifacts do we get from the iFFT(FFT(PSF)) - PSF ?
    # p = np.fft.irfft2(P, s=(pH,pW))
    # plt.clf()
    # plt.subplot(1,3,1)
    # # ASSUME PixelizedPSF
    # pad,cx,cy = psf._padInImage(pW,pH)
    # plt.imshow(pad, interpolation='nearest', origin='lower')
    # plt.subplot(1,3,2)
    # plt.imshow(p, interpolation='nearest', origin='lower')
    # plt.subplot(1,3,3)
    # plt.imshow(pad - p, interpolation='nearest', origin='lower')
    # plt.colorbar()
    # debug_ps.savefig()

    # plt.clf()
    # plt.subplot(1,2,1)
    # mx = np.abs(P).max()
    # plt.imshow(np.log10(np.abs(P)),
    #            interpolation='nearest', origin='lower')
    # plt.xticks([]); plt.yticks([])
    # plt.colorbar()
    # plt.title('log(abs(FFT(PSF)))')
    # plt.subplot(1,2,2)
    # plt.imshow(np.arctan2(P.imag, P.real),
    #            vmin=-np.pi, vmax=np.pi,
    #            interpolation='nearest', origin='lower', cmap='RdBu')
    # plt.xticks([]); plt.yticks([])
    # plt.title('phase(FFT(PSF))')
    # debug_ps.savefig()

class HoggGalaxy(ProfileGalaxy, Galaxy):

    def getName(self):
        return 'HoggGalaxy'

    def getRadius(self):
        return self.nre * self.shape.re

    def _getAffineProfile(self, img, px, py):
        ''' Returns a MixtureOfGaussians profile that has been
        affine-transformed into the pixel space of the image.
        '''
        galmix = self.getProfile()
        cdinv = img.getWcs().cdInverseAtPixel(px, py)
        G = self.shape.getRaDecBasis()
        Tinv = np.dot(cdinv, G)
        amix = galmix.apply_affine(np.array([px, py]), Tinv)
        return amix

    def _getShearedProfile(self, img, px, py):
        ''' Returns a MixtureOfGaussians profile that has been
        shear-transformed into the pixel space of the image.
        At px,py (but not offset to px,py).
        '''
        galmix = self.getProfile()
        cdinv = img.getWcs().cdInverseAtPixel(px, py)
        G = self.shape.getRaDecBasis()
        Tinv = np.dot(cdinv, G)
        amix = galmix.apply_shear(Tinv)
        return amix

    def _getUnitFluxDeps(self, img, px, py):
        # return ('unitpatch', self.getName(), px, py,
        return hash(('unitpatch', self.getName(), px, py,
                     img.getWcs().hashkey(),
                     img.getPsf().hashkey(), self.shape.hashkey())
                    )

    def _getUnitFluxPatchSize(self, img, px=0., py=0., minval=0.):
        if hasattr(self, 'halfsize'):
            return self.halfsize
        pixscale = img.wcs.pixscale_at(px, py)
        halfsize = max(1., self.getRadius() / pixscale)
        halfsize += img.psf.getRadius()
        halfsize = int(np.ceil(halfsize))
        return halfsize

class GaussianGalaxy(HoggGalaxy):
    nre = 6.
    profile = mp.MixtureOfGaussians(np.array([1.]), np.zeros((1, 2)),
                                    np.array([[[1., 0.], [0., 1.]]]))
    profile.normalize()

    def __init__(self, *args, **kwargs):
        self.nre = GaussianGalaxy.nre
        super(GaussianGalaxy, self).__init__(*args, **kwargs)

    def getName(self):
        return 'GaussianGalaxy'

    def getProfile(self):
        return GaussianGalaxy.profile

class ExpGalaxy(HoggGalaxy):
    nre = 4.
    profile = mp.get_exp_mixture()
    profile.normalize()

    @staticmethod
    def getExpProfile():
        return ExpGalaxy.profile

    def __init__(self, *args, **kwargs):
        self.nre = ExpGalaxy.nre
        super(ExpGalaxy, self).__init__(*args, **kwargs)

    def getName(self):
        return 'ExpGalaxy'

    def getProfile(self):
        return ExpGalaxy.profile

class DevGalaxy(HoggGalaxy):
    nre = 8.
    profile = mp.get_dev_mixture()
    profile.normalize()

    @staticmethod
    def getDevProfile():
        return DevGalaxy.profile

    def __init__(self, *args, **kwargs):
        self.nre = DevGalaxy.nre
        super(DevGalaxy, self).__init__(*args, **kwargs)

    def getName(self):
        return 'DevGalaxy'

    def getProfile(self):
        return DevGalaxy.profile

class FracDev(ScalarParam):
    stepsize = 0.01

    def clipped(self):
        f = self.getValue()
        return np.clip(f, 0., 1.)

    def derivative(self):
        return 1.

class SoftenedFracDev(FracDev):
    '''
    Implements a "softened" version of the deV-to-total fraction.

    The sigmoid function is scaled and shifted so that S(0) ~ 0.1 and
    S(1) ~ 0.9.

    Use the 'clipped()' function to get the un-softened fracDev
    clipped to [0,1].
    '''

    def clipped(self):
        f = self.getValue()
        return 1. / (1 + np.exp(4. * (0.5 - f)))

    def derivative(self):
        f = self.getValue()
        # Thanks, Sage
        ef = np.exp(-4. * f + 2)
        return 4. * ef / ((ef + 1)**2)

class FixedCompositeGalaxy(MultiParams, ProfileGalaxy, SingleProfileSource):
    '''
    A galaxy with Exponential and deVaucouleurs components where the
    brightnesses of the deV and exp components are defined in terms of
    a total brightness and a fraction of that total that goes to the
    deV component.

    The two components share a position (ie the centers are the same),
    but have different shapes.  The galaxy has a single brightness
    that is split between the components.

    This is like CompositeGalaxy, but more useful for getting
    consistent colors from forced photometry, because one can freeze
    the fracDev to keep the deV+exp profile fixed.
    '''

    def __init__(self, pos, brightness, fracDev, shapeExp, shapeDev):
        # handle passing fracDev as a float
        if not isinstance(fracDev, BaseParams):
            fracDev = FracDev(fracDev)
        MultiParams.__init__(self, pos, brightness, fracDev,
                             shapeExp, shapeDev)
        self.name = self.getName()

    def getRadius(self):
        return max(self.shapeExp.re * ExpGalaxy.nre,
                   self.shapeDev.re * DevGalaxy.nre)

    @staticmethod
    def getNamedParams():
        return dict(pos=0, brightness=1, fracDev=2, shapeExp=3, shapeDev=4)

    def getName(self):
        return 'FixedCompositeGalaxy'

    def __str__(self):
        return (self.name + ' at ' + str(self.pos)
                + ' with ' + str(self.brightness)
                + ', ' + str(self.fracDev)
                + ', exp ' + str(self.shapeExp)
                + ', deV ' + str(self.shapeDev))

    def __repr__(self):
        return (self.name + '(pos=' + repr(self.pos) +
                ', brightness=' + repr(self.brightness) +
                ', fracDev=' + repr(self.fracDev) +
                ', shapeExp=' + repr(self.shapeExp) +
                ', shapeDev=' + repr(self.shapeDev) + ')')

    def _getAffineProfile(self, img, px, py):
        # f = self.fracDev.clipped()
        # profs = []
        # if f > 0.:
        #     profs.append((f, DevGalaxy.profile, self.shapeDev))
        # if f < 1.:
        #     profs.append((1. - f, ExpGalaxy.profile, self.shapeExp))
        # cdinv = img.getWcs().cdInverseAtPixel(px, py)
        # mix = []
        # for f, p, s in profs:
        #     G = s.getRaDecBasis()
        #     Tinv = np.dot(cdinv, G)
        #     amix = p.apply_affine(np.array([px, py]), Tinv)
        #     amix.amp = amix.amp * f
        #     mix.append(amix)

        f = self.fracDev.clipped()
        cdinv = img.getWcs().cdInverseAtPixel(px, py)
        mix = []
        if f > 0.:
            amix = DevGalaxy.profile.apply_affine(np.array([px, py]), np.dot(cdinv, self.shapeDev.getRaDecBasis()))
            amix.amp = amix.amp * f
            mix.append(amix)
        if f < 1.:
            amix = ExpGalaxy.profile.apply_affine(np.array([px, py]), np.dot(cdinv, self.shapeExp.getRaDecBasis()))
            amix.amp = amix.amp * (1. - f)
            mix.append(amix)
        if len(mix) == 1:
            return mix[0]
        return mix[0] + mix[1]

    def _getShearedProfile(self, img, px, py):
        ''' Returns a MixtureOfGaussians profile that has been
        shear-transformed into the pixel space of the image.
        At px,py (but not offset to px,py).
        '''
        # f = self.fracDev.clipped()
        # profs = []
        # if f > 0.:
        #     profs.append((f, DevGalaxy.profile, self.shapeDev))
        # if f < 1.:
        #     profs.append((1. - f, ExpGalaxy.profile, self.shapeExp))
        # cdinv = img.getWcs().cdInverseAtPixel(px, py)
        # mix = []
        # for f, p, s in profs:
        #     G = s.getRaDecBasis()
        #     Tinv = np.dot(cdinv, G)
        #     amix = p.apply_shear(Tinv)
        #     amix.amp = amix.amp * f
        #     mix.append(amix)
        # if len(mix) == 1:
        #     return mix[0]
        # return mix[0] + mix[1]

        f = self.fracDev.clipped()
        cdinv = img.getWcs().cdInverseAtPixel(px, py)
        mix = []
        if f > 0.:
            amix = DevGalaxy.profile.apply_shear(np.dot(cdinv, self.shapeDev.getRaDecBasis()))
            amix.amp = amix.amp * f
            mix.append(amix)
        if f < 1.:
            amix = ExpGalaxy.profile.apply_shear(np.dot(cdinv, self.shapeExp.getRaDecBasis()))
            amix.amp = amix.amp * (1. - f)
            mix.append(amix)
        if len(mix) == 1:
            return mix[0]
        return mix[0] + mix[1]

    def _getUnitFluxPatchSize(self, img, px=0., py=0., minval=0.):
        if hasattr(self, 'halfsize'):
            return self.halfsize
        pixscale = img.wcs.pixscale_at(px, py)
        f = self.fracDev.clipped()
        r = 1.
        if f < 1.:
            s = self.shapeExp
            rexp = ExpGalaxy.nre * s.re
            r = max(r, rexp)
        if f > 0.:
            s = self.shapeDev
            rdev = max(r, DevGalaxy.nre * s.re)
            r = max(r, rdev)
        halfsize = r / pixscale
        halfsize += img.psf.getRadius()
        return halfsize

    def _getUnitFluxDeps(self, img, px, py):
        return hash(('unitpatch', self.getName(),
                     px, py, img.getWcs().hashkey(),
                     img.getPsf().hashkey(),
                     self.shapeDev.hashkey(),
                     self.shapeExp.hashkey(),
                     self.fracDev.hashkey()))

    def getParamDerivatives(self, img, modelMask=None, **kwargs):
        e = ExpGalaxy(self.pos, self.brightness, self.shapeExp)
        d = DevGalaxy(self.pos, self.brightness, self.shapeDev)
        e.dname = 'fcomp.exp'
        d.dname = 'fcomp.dev'

        if self.isParamFrozen('pos'):
            e.freezeParam('pos')
            d.freezeParam('pos')
        if self.isParamFrozen('shapeExp'):
            e.freezeParam('shape')
        if self.isParamFrozen('shapeDev'):
            d.freezeParam('shape')

        if hasattr(self, 'halfsize'):
            e.halfsize = self.halfsize
            d.halfsize = self.halfsize

        dexp = e.getParamDerivatives(img, modelMask=modelMask, **kwargs)
        ddev = d.getParamDerivatives(img, modelMask=modelMask, **kwargs)

        # print('FixedCompositeGalaxy.getParamDerivatives.')
        # print('tim shape', img.shape)
        # print('exp deriv extents:')
        # for deriv in dexp + ddev:
        #     print('  ', deriv.name, deriv.getExtent())

        # fracDev scaling
        f = self.fracDev.clipped()
        for deriv in dexp:
            if deriv is not None:
                deriv *= (1. - f)
        for deriv in ddev:
            if deriv is not None:
                deriv *= f

        derivs = []
        i0 = 0
        if not self.isParamFrozen('pos'):
            # "pos" is shared between the models, so add the derivs.
            npos = self.pos.numberOfParams()
            for i in range(npos):
                ii = i0 + i
                dsum = add_patches(dexp[ii], ddev[ii])
                if dsum is not None:
                    dsum.setName('d(fcomp)/d(pos%i)' % i)
                derivs.append(dsum)
            i0 += npos

        if not self.isParamFrozen('brightness'):
            # shared between the models, so add the derivs.
            nb = self.brightness.numberOfParams()
            for i in range(nb):
                ii = i0 + i
                dsum = add_patches(dexp[ii], ddev[ii])
                if dsum is not None:
                    dsum.setName('d(fcomp)/d(bright%i)' % i)
                derivs.append(dsum)
            i0 += nb

        if not self.isParamFrozen('fracDev'):
            counts = img.getPhotoCal().brightnessToCounts(self.brightness)
            if counts == 0.:
                derivs.append(None)
            else:
                # FIXME -- should be possible to avoid recomputing these...
                ue = e.getUnitFluxModelPatch(img, modelMask=modelMask, **kwargs)
                ud = d.getUnitFluxModelPatch(img, modelMask=modelMask, **kwargs)

                df = self.fracDev.derivative()

                if ue is not None:
                    ue *= -df
                if ud is not None:
                    ud *= +df
                df = add_patches(ud, ue)
                if df is None:
                    derivs.append(None)
                else:
                    df *= counts
                    df.setName('d(fcomp)/d(fracDev)')
                    derivs.append(df)

        if not self.isParamFrozen('shapeExp'):
            derivs.extend(dexp[i0:])
        if not self.isParamFrozen('shapeDev'):
            derivs.extend(ddev[i0:])
        return derivs

class CompositeGalaxy(MultiParams, BasicSource):
    '''
    A galaxy with Exponential and deVaucouleurs components.

    The two components share a position (ie the centers are the same),
    but have different brightnesses and shapes.
    '''

    def __init__(self, pos, brightnessExp, shapeExp, brightnessDev, shapeDev):
        MultiParams.__init__(self, pos, brightnessExp, shapeExp,
                             brightnessDev, shapeDev)
        self.name = self.getName()

    @staticmethod
    def getNamedParams():
        return dict(pos=0, brightnessExp=1, shapeExp=2,
                    brightnessDev=3, shapeDev=4)

    def getName(self):
        return 'CompositeGalaxy'

    def __str__(self):
        return (self.name + ' at ' + str(self.pos)
                + ' with Exp ' + str(self.brightnessExp) + ' '
                + str(self.shapeExp)
                + ' and deV ' + str(self.brightnessDev) + ' '
                + str(self.shapeDev))

    def __repr__(self):
        return (self.name + '(pos=' + repr(self.pos) +
                ', brightnessExp=' + repr(self.brightnessExp) +
                ', shapeExp=' + repr(self.shapeExp) +
                ', brightnessDev=' + repr(self.brightnessDev) +
                ', shapeDev=' + repr(self.shapeDev))

    def getBrightness(self):
        ''' This makes some assumptions about the
        ``Brightness`` / ``PhotoCal`` and should be treated as
        approximate.'''
        return self.brightnessExp + self.brightnessDev

    def getBrightnesses(self):
        return [self.brightnessExp, self.brightnessDev]

    def _getModelPatches(self, img, minsb=0., modelMask=None, **kwargs):
        e = ExpGalaxy(self.pos, self.brightnessExp, self.shapeExp)
        d = DevGalaxy(self.pos, self.brightnessDev, self.shapeDev)
        kw = kwargs.copy()
        if minsb:
            kw.update(minsb=minsb / 2.)
        if hasattr(self, 'halfsize'):
            e.halfsize = d.halfsize = self.halfsize
        pe = e.getModelPatch(img, modelMask=modelMask, **kw)
        pd = d.getModelPatch(img, modelMask=modelMask, **kw)
        return (pe, pd)

    def getModelPatch(self, img, minsb=0., modelMask=None, **kwargs):
        pe, pd = self._getModelPatches(img, minsb=minsb, modelMask=modelMask,
                                       **kwargs)
        return add_patches(pe, pd)

    def getUnitFluxModelPatches(self, img, minval=0., modelMask=None, **kwargs):
        # Needed for forced photometry
        if minval > 0:
            # allow each component half the error
            minval = minval * 0.5
        e = ExpGalaxy(self.pos, self.brightnessExp, self.shapeExp)
        d = DevGalaxy(self.pos, self.brightnessDev, self.shapeDev)
        if hasattr(self, 'halfsize'):
            e.halfsize = d.halfsize = self.halfsize
        return (e.getUnitFluxModelPatches(img, minval=minval,
                                          modelMask=modelMask, **kwargs) +
                d.getUnitFluxModelPatches(img, minval=minval,
                                          modelMask=modelMask, **kwargs))

    # MAGIC: ORDERING OF EXP AND DEV PARAMETERS
    # MAGIC: ASSUMES EXP AND DEV SHAPES SAME LENGTH
    # CompositeGalaxy.
    def getParamDerivatives(self, img, modelMask=None, **kwargs):
        e = ExpGalaxy(self.pos, self.brightnessExp, self.shapeExp)
        d = DevGalaxy(self.pos, self.brightnessDev, self.shapeDev)
        if hasattr(self, 'halfsize'):
            e.halfsize = d.halfsize = self.halfsize
        e.dname = 'comp.exp'
        d.dname = 'comp.dev'
        if self.isParamFrozen('pos'):
            e.freezeParam('pos')
            d.freezeParam('pos')
        if self.isParamFrozen('brightnessExp'):
            e.freezeParam('brightness')
        if self.isParamFrozen('shapeExp'):
            e.freezeParam('shape')
        if self.isParamFrozen('brightnessDev'):
            d.freezeParam('brightness')
        if self.isParamFrozen('shapeDev'):
            d.freezeParam('shape')

        de = e.getParamDerivatives(img, modelMask=modelMask, **kwargs)
        dd = d.getParamDerivatives(img, modelMask=modelMask, **kwargs)

        if self.isParamFrozen('pos'):
            derivs = de + dd
        else:
            derivs = []
            # "pos" is shared between the models, so add the derivs.
            npos = len(self.pos.getStepSizes())
            for i in range(npos):
                dp = add_patches(de[i], dd[i])
                if dp is not None:
                    dp.setName('d(comp)/d(pos%i)' % i)
                derivs.append(dp)
            derivs.extend(de[npos:])
            derivs.extend(dd[npos:])

        return derivs


def edit_distance(x, y):
    # Create distance matrix
    D = []
    for i in range(len(x)+1):
        D.append([0]*(len(y)+1))
    # Initialize first row and column of matrix
    for i in range(len(x)+1):
        D[i][0] = i

    # Fill in the rest of the matrix
    for i in range(1, len(x)+1):
        for j in range(1, len(y)+1):
            distHor = D[i][j-1] + 1
            distVer = D[i-1][j] + 1
            if x[i-1] == y[j-1]:
                distDiag = D[i-1][j-1]
            else:
                distDiag = D[i-1][j-1] + 1
            D[i][j] = min(distHor, distVer, distDiag)
    # Edit distance is the value in the bottom right corner of the matrix
    return min(D[-1])

from python.assignment_1 import read_genome

t  = read_genome('data/week_3.fasta')

'''
We saw how to adapt dynamic programming to find approximate occurrences of a pattern in a text. Recall that:

    Rows of the dynamic programming matrix are labeled with bases from P and columns with bases from T
    Elements in the first row are set to 0
    Elements in the first column are set to 0, 1, 2, ..., as for edit distance
    Other elements are set in the same way as elements of a standard edit distance matrix
    The minimal value in the bottom row is the edit distance of the closest match between P and T

First, download the provided excerpt of human chromosome 1

Second, parse it using the read_genome function we wrote before.

Third, adapt the editDistance function we saw in practical (copied below) to answer questions 1 and 2 below.
Your function should take arguments p (pattern), t (text) and should return the edit distance of the match
between P and T with the fewest edits.

int: In the "A new solution to approximate matching" video we saw that the best approximate match of
P = GCGTATGC  within T = TATTGGCTATACGGTT  had 2 edits. You can use this and other small examples
to double-check that your function is working.
'''

# edit_distance('GATTTACCAGATTGAG', t)

def overlap(a, b, min_length=3):
    """ Return length of longest suffix of 'a' matching
        a prefix of 'b' that is at least 'min_length'
        characters long.  If no such overlap exists,
        return 0. """
    start = 0  # start all the way at the left
    while True:
        start = a.find(b[:min_length], start)  # look for b's prefix in a
        if start == -1:  # no more occurrences to right
            return 0
        # found occurrence; check for full suffix/prefix match
        if b.startswith(a[start:]):
            return len(a)-start
        start += 1  # move just past previous match

from python.assignment_1 import read_fastq

data = read_fastq('data/week_3_hw.fastq')


from socket import socket

# The socket used by the main program to communicate with the AppIndicatorflow
class ClientSocket:
    PORT = 9999
    s: socket = None

    def __init__(self) -> None:
        import os
        pid = os.getpid()
        s = socket()

        s.connect(('localhost', self.PORT))

        message=("pid:" + str(pid)).encode()
        s.send(message)

        self.s = s

    def check_to_send(self, prev, curr, msg) -> None:
        msg = ''.join(msg)

        if msg == "quit application":
            self.s.send(("quit application").encode())
            self.end_socket()
            return
        if prev != curr:
            print("sending \'" + msg + "\' to update appindicator")
            self.s.send(msg.encode())

    def end_socket(self):
        if self.s is not None:
            self.s.close()
            self.s = None

    def force_quit(self):
        self.s.send("quit application".encode())
        self.end_socket()