"""The Game of Hog."""

from dice import six_sided, make_test_dice
from ucb import main, trace, interact
from math import log2

GOAL = 100  # The goal of Hog is to score 100 points.

######################
# Phase 1: Simulator #
######################

def roll_dice(num_rolls, dice=six_sided):
    """Simulate rolling the DICE exactly NUM_ROLLS > 0 times. Return the sum of
    the outcomes unless any of the outcomes is 1. In that case, return 1.

    num_rolls:  The number of dice rolls that will be made.
    dice:       A function that simulates a single dice roll outcome.
    """

    # BEGIN PROBLEM 1
    # These assert statements ensure that num_rolls is a positive integer.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls > 0, 'Must roll at least once.'
    dice_sum = 0
    rolled_one = False
    for i in range(num_rolls):
        dice_result = dice()    # roll the dice
        if dice_result == 1:
            rolled_one = True  # Set a flag that a 1 was rolled
        else:
            dice_sum += dice_result
    if rolled_one:
        return 1                    # return 1 if a 1 was rolled
    else:
        return dice_sum               # return the sum
    # END PROBLEM 1

def tail_points(opponent_score):
    """Return the points scored by rolling 0 dice according to Pig Tail.

    opponent_score:   The total score of the other player.

    """
    # BEGIN PROBLEM 2
    # Ensure opponent_score is only two digits
    opponent_score %= 100
    # Split the opponent's score into tens and ones digits
    tens, ones = divmod(opponent_score, 10)
    # Calculate the score based on the rules of Pig Tail
    pig_tail_score = 2 * abs(tens - ones) + 1
    return pig_tail_score   # Return the score
    # END PROBLEM 2

def take_turn(num_rolls, opponent_score, dice=six_sided):
    """Return the points scored on a turn rolling NUM_ROLLS dice when the
    opponent has OPPONENT_SCORE points.

    num_rolls:       The number of dice rolls that will be made.
    opponent_score:  The total score of the other player.
    dice:            A function that simulates a single dice roll outcome.
    """
    # Leave these assert statements here; they help check for errors.
    assert type(num_rolls) == int, 'num_rolls must be an integer.'
    assert num_rolls >= 0, 'Cannot roll a negative number of dice in take_turn.'
    assert num_rolls <= 10, 'Cannot roll more than 10 dice.'
    # BEGIN PROBLEM 3
    "*** YOUR CODE HERE ***"
    if num_rolls == 0:
        return tail_points(opponent_score)
    else:
        return roll_dice(num_rolls, dice)
    # END PROBLEM 3

def simple_update(num_rolls, player_score, opponent_score, dice=six_sided):
    """Return the total score of a player who starts their turn with
    PLAYER_SCORE and then rolls NUM_ROLLS DICE, ignoring Square Swine.
    """
    return player_score + take_turn(num_rolls, opponent_score, dice)

def square_update(num_rolls, player_score, opponent_score, dice=six_sided):
    """Return the total score of a player who starts their turn with
    PLAYER_SCORE and then rolls NUM_ROLLS DICE, *ipython3 ok -q 01ncluding* Square Swine.
    """
    score = player_score + take_turn(num_rolls, opponent_score, dice)
    if perfect_square(score):  # Implement perfect_square
        return next_perfect_square(score)  # Implement next_perfect_square
    else:
        return score

# BEGIN PROBLEM 4
def perfect_square(score):
    """Return True if score is a perfect square, False otherwise."""
    return score ** 0.5 % 1 == 0

def next_perfect_square(score):
    """Return the next perfect square after score."""
    return int(score ** 0.5 + 1) ** 2
# END PROBLEM 4

def always_roll_5(score, opponent_score):
    """A strategy of always rolling 5 dice, regardless of the player's score or
    the oppononent's score.
    """
    return 5

def play(strategy0, strategy1, update,
         score0=0, score1=0, dice=six_sided, goal=GOAL):
    """Simulate a game and return the final scores of both players, with
    Player 0's score first and Player 1's score second.

    E.g., play(always_roll_5, always_roll_5, square_update) simulates a game in
    which both players always choose to roll 5 dice on every turn and the Square
    Swine rule is in effect.

    A strategy function, such as always_roll_5, takes the current player's
    score and their opponent's score and returns the number of dice the current
    player chooses to roll.

    An update function, such as square_update or simple_update, takes the number
    of dice to roll, the current player's score, the opponent's score, and the
    dice function used to simulate rolling dice. It returns the updated score
    of the current player after they take their turn.

    strategy0: The strategy for player0.
    strategy1: The strategy for player1.
    update:    The update function (used for both players).
    score0:    Starting score for Player 0
    score1:    Starting score for Player 1
    dice:      A function of zero arguments that simulates a dice roll.
    goal:      The game ends and someone wins when this score is reached.
    """
    who = 0  # Who is about to take a turn, 0 (first) or 1 (second)
    # BEGIN PROBLEM 5
    while score0 < goal and score1 < goal:  # While neither player has won
        if who == 0:  # If it's player 0's turn
            score0 = update(strategy0(score0, score1), score0,
                            score1, dice)  # Update player 0's score
        else:  # If it's player 1's turn
            score1 = update(strategy1(score1, score0), score1,
                            score0, dice)  # Update player 1's score
        who = 1 - who  # Switch players
    return score0, score1
    # END PROBLEM 5

#######################
# Phase 2: Strategies #
#######################

def always_roll(n):
    """Return a player strategy that always rolls N dice.

    A player strategy is a function that takes two total scores as arguments
    (the current player's score, and the opponent's score), and returns a
    number of dice that the current player will roll this turn.

    >>> strategy = always_roll(3)
    >>> strategy(0, 0)
    3
    >>> strategy(99, 99)
    3
    """
    assert n >= 0 and n <= 10
    # BEGIN PROBLEM 6
    "*** YOUR CODE HERE ***"
    def strategy(score, opponent_score):
        return n

    return strategy
    # END PROBLEM 6

def catch_up(score, opponent_score):
    """A player strategy that always rolls 5 dice unless the opponent
    has a higher score, in which case 6 dice are rolled.

    >>> catch_up(9, 4)
    5
    >>> strategy(17, 18)
    6
    """
    if score < opponent_score:
        return 6  # Roll one more to catch up
    else:
        return 5

def is_always_roll(strategy, goal=GOAL):
    """Return whether strategy always chooses the same number of dice to roll.

    >>> is_always_roll(always_roll_5)
    True
    >>> is_always_roll(always_roll(3))
    True
    >>> is_always_roll(catch_up)
    False
    """
    # BEGIN PROBLEM 7
    # get the number of dice to roll at the start
    first_result = strategy(0, 0)
    for i in range(goal):  # check across a range of possible scores
        for j in range(goal):
            if strategy(i, j) != first_result:  # if the strategy rolls a different number of dice
                return False  # then it does not always roll the same number of dice
    return True  # if we made it through all the scores without returning, the strategy always rolls the same number of dice
    # END PROBLEM 7

def make_averaged(original_function, total_samples=1000):
    """Return a function that returns the average value of ORIGINAL_FUNCTION
    called TOTAL_SAMPLES times.

    To implement this function, you will have to use *args syntax.

    >>> dice = make_test_dice(4, 2, 5, 1)
    >>> averaged_dice = make_averaged(roll_dice, 40)
    >>> averaged_dice(1, dice)  # The avg of 10 4's, 10 2's, 10 5's, and 10 1's
    3.0
    """
    # BEGIN PROBLEM 8
    def averaged(*args):
        total = 0
        for i in range(total_samples):
            total += original_function(*args)
        return total / total_samples
    return averaged
    # END PROBLEM 8

def max_scoring_num_rolls(dice=six_sided, total_samples=1000):
    """Return the number of dice (1 to 10) that gives the highest average turn score
    by calling roll_dice with the provided DICE a total of TOTAL_SAMPLES times.
    Assume that the dice always return positive outcomes.

    >>> dice = make_test_dice(1, 6)
    >>> max_scoring_num_rolls(dice)
    1
    """
    # BEGIN PROBLEM 9
    max_score = 0
    max_rolls = 0
    for i in range(1, 11):
        average = make_averaged(roll_dice, total_samples)(i, dice)
        if average > max_score:
            max_score = average
            max_rolls = i
    return max_rolls
    # END PROBLEM 9

def winner(strategy0, strategy1):
    """Return 0 if strategy0 wins against strategy1, and 1 otherwise."""
    score0, score1 = play(strategy0, strategy1, square_update)
    if score0 > score1:
        return 0
    else:
        return 1

def average_win_rate(strategy, baseline=always_roll(6)):
    """Return the average win rate of STRATEGY against BASELINE. Averages the
    winrate when starting the game as player 0 and as player 1.
    """
    win_rate_as_player_0 = 1 - make_averaged(winner)(strategy, baseline)
    win_rate_as_player_1 = make_averaged(winner)(baseline, strategy)

    return (win_rate_as_player_0 + win_rate_as_player_1) / 2

def run_experiments():
    """Run a series of strategy experiments and report results."""
    six_sided_max = max_scoring_num_rolls(six_sided)
    print('Max scoring num rolls for six-sided dice:', six_sided_max)

    print('always_roll(6) win rate:',
          average_win_rate(always_roll(6)))  # near 0.5
    print('catch_up win rate:', average_win_rate(catch_up))
    print('always_roll(3) win rate:', average_win_rate(always_roll(3)))
    print('always_roll(8) win rate:', average_win_rate(always_roll(8)))

    print('tail_strategy win rate:', average_win_rate(tail_strategy))
    print('square_strategy win rate:', average_win_rate(square_strategy))
    print('final_strategy win rate:', average_win_rate(final_strategy))
    "*** You may add additional experiments as you wish ***"

def tail_strategy(score, opponent_score, threshold=12, num_rolls=6):
    """This strategy returns 0 dice if Pig Tail gives at least THRESHOLD
    points, and returns NUM_ROLLS otherwise. Ignore score and Square Swine.
    """
    # BEGIN PROBLEM 10
    if tail_points(opponent_score) >= threshold:
        return 0
    else:
        return num_rolls
    # END PROBLEM 10

def square_strategy(score, opponent_score, threshold=12, num_rolls=6):
    """This strategy returns 0 dice when your score would increase by at least threshold."""
    # BEGIN PROBLEM 11
    if square_update(0, score, opponent_score) - score >= threshold:
        return 0
    else:
        return num_rolls
    # END PROBLEM 11

def final_strategy(score, opponent_score):
    """Write a brief description of your final strategy.
    if you can win with 0 rolls, than do it.
    """
    # BEGIN PROBLEM 12
    if square_update(0, score, opponent_score) >= GOAL:
        return 0
    elif square_update(0, score, opponent_score) > simple_update(6, score, opponent_score):
        return 0
    else:
        return 6
    # END PROBLEM 12

##########################
# Command Line Interface #
##########################

# NOTE: The function in this section does not need to be changed. It uses
# features of Python not yet covered in the course.

@main
def run(*args):
    """Read in the command-line argument and calls corresponding functions."""
    import argparse
    parser = argparse.ArgumentParser(description="Play Hog")
    parser.add_argument('--run_experiments', '-r', action='store_true',
                        help='Runs strategy experiments')

    args = parser.parse_args()

    if args.run_experiments:
        run_experiments()


def ArrayRot(arr,d,n):   #d means how many elements to rotate

    for i in range(d):
        leftrot(arr,n)

def leftrot(arr,n):
    temp=arr[0]
    for i in range(n-1):
        arr[i]=arr[i+1]

    arr[n-1]=temp

def printf(arr):
    print(arr)

arr=[2,3,4,5,8,9]
d=2
n=len(arr)
ArrayRot(arr,d,n)
printf(arr)


from map.campaign import SafetyFeatures, Testimonials, Innovation, Models, TopPanel, TopPanelOptions, Cookies

class PageMap:
    '''Page Map class

        - The class is used to wire/map the locators in each page to the corresponding feature steps.
        - It is implemented using Python dictionary action_map
        - For each element there is a key, to identify the locator type for promoting action reuse.
        - While adding/updating any keys in this class, the convention is to use the following order :
            App:Page:Element:Locator
        '''

    action_map ={'Campaign page':
            {'Cookies alert':{
                'Heading': {'locator': Cookies.header, 'type': "xpath"},
                'Accept': {'locator': Cookies.accept, 'type': "xpath"},},
            'Safety Section': {
                'Heading': {'locator': SafetyFeatures.header, 'type': "xpath"},
                'Safety header': {'locator': SafetyFeatures.safety_header, 'type': "xpath"},
                'Safety video': {'locator': SafetyFeatures.safety_video, 'type': "xpath"},
                'Speed section header': {'locator': SafetyFeatures.speed_header, 'type': "xpath"},
                'Speed cap': {'locator': SafetyFeatures.speed_cap, 'type': "xpath"},
                'Highway pilot': {'locator': SafetyFeatures.highway_pilot, 'type': "xpath"},
                'Driver monitoring cameras': {'locator': SafetyFeatures.driver_monitor, 'type': "xpath"},
                'Care key': {'locator': SafetyFeatures.care_key, 'type': "xpath"},
                'Learn more URL': {'locator': SafetyFeatures.learn_more, 'type': "xpath"},
                'Conditions message': {'locator': SafetyFeatures.conditions, 'type': "xpath"},},
            'Testimonial Section': {
                'Heading': {'locator': Testimonials.header, 'type': "xpath"},
                'Testimonial header': {'locator': Testimonials.tesimonial_header, 'type': "xpath"},
                'Testimonial description': {'locator': Testimonials.testomonial_description, 'type': "xpath"},
                'Amy Video': {'locator': Testimonials.amy_video, 'type': "xpath"},
                'Amy': {'locator': Testimonials.amy, 'type': "xpath"},
                'Amy Description': {'locator': Testimonials.amy_description, 'type': "xpath"},},
            'Innovation Section': {
                'Heading': {'locator': Innovation.header, 'type': "xpath"},
                'Innovation header': {'locator': Innovation.innovation_header, 'type': "xpath"},
                'Innovation description': {'locator': Innovation.innovation_description, 'type': "xpath"},
                'Learn more URL': {'locator': Innovation.learn_more, 'type': "xpath"},
                'Innovation image': {'locator': Innovation.innovation_image, 'type': "xpath"},},
            'Models Section': {
                'Heading': {'locator': Models.header, 'type': "xpath"},
                'Models Header': {'locator': Models.models_header, 'type': "xpath"},
                'XC90 SUV Header': {'locator': Models.xc_90_header, 'type': "xpath"},
                'XC90': {'locator': Models.xc_90, 'type': "xpath"},
                'XC90 Image': {'locator': Models.xc_90_image, 'type': "xpath"},
                'XC90 Learn': {'locator': Models.xc_90_learn, 'type': "xpath"},
                'XC90 Shop': {'locator': Models.xc_90_shop, 'type': "xpath"},
                'Learn More': {'locator': Models.learn_more, 'type': "xpath"},
                'Mild Hybrid Cars': {'locator': Models.mild_hybrid, 'type': "xpath"},},
            'Top Panel Section': {
                'Heading': {'locator': TopPanel.header, 'type': "xpath"},
                'Volvo logo': {'locator': TopPanel.volvo_logo, 'type': "xpath"},
                'Our Cars URL': {'locator': TopPanel.our_cars, 'type': "xpath"},
                'Options': {'locator': TopPanel.options, 'type': "xpath"},},
            'Top Panel options Section': {
                'Heading': {'locator': TopPanelOptions.header, 'type': "xpath"},
                'Volvo logo': {'locator': TopPanelOptions.volvo_logo, 'type': "xpath"},
                'Buy': {'locator': TopPanelOptions.buy, 'type': "xpath"},
                'Own': {'locator': TopPanelOptions.own, 'type': "xpath"},
                'Why Volvo': {'locator': TopPanelOptions.why_volvo, 'type': "xpath"},
                'Explore': {'locator': TopPanelOptions.explore, 'type': "xpath"},
                'More': {'locator': TopPanelOptions.more, 'type': "xpath"},
                'International': {'locator': TopPanelOptions.international, 'type': "xpath"},
                'Facebook': {'locator': TopPanelOptions.facebook, 'type': "xpath"},},
            'Buy Section': {
                'Heading': {'locator': TopPanelOptions.header, 'type': "xpath"},
                'Purchase Header': {'locator': TopPanelOptions.purchase, 'type': "xpath"},
                'Fleet sales': {'locator': TopPanelOptions.fleet_sales, 'type': "xpath"},
                'Used cars': {'locator': TopPanelOptions.used_sales, 'type': "xpath"},},},}


# This class represents bowling data from a round of bowling.
# The number of frames in the round can be specified. The default is 10.
# It is a container for bowling frames, and instances can add frames until
# the number of frames in the round are complete.
# It is considered complete when all frames for the round have been added.
# If complete, there is a method to retrieve the total score.
# There are also methods to print the scores from the frames to the console
# or an output file.
# An alternate way of adding a complete dataset is provided which requires
# no user input, but instead a nested list that contains score information.
class BowlingData:
    nFramesPerRound = 10
    def __init__(self):
        self.frames = []
        self.nFrames = 0
        self.scoreIndByFrame = []
        self.scores = []
        self.totalScore = 0
        self.complete = False

    def reset(self):
        self.__init__()

    def isComplete(self):
        return self.complete

    def addFrame(self):
        if self.complete:
            print("Cannot add another frame. Score information for all " + str(self.__nFramesPerRound) + " frames is complete.")
        else:
            # Add new frame, check whether to add a regular frame or last frame object.
            if self.nFrames == BowlingData.nFramesPerRound - 1:
                newFrame = LastFrame(self.nFrames+1)
            else:
                newFrame = RegularFrame(self.nFrames+1)
            while not(newFrame.isComplete()):
                newFrame.addShot()
            self.frames.append(newFrame)
            self.nFrames += 1

            # update attributes scoreIndByFrame and scores
            frameShots = self.frames[-1].getShots()
            shotInd = []
            shotScores = []
            shotIndexStart = len(self.scores)
            for i in range(len(frameShots)):
                shotInd.append(shotIndexStart + i)
                shotScores.append(frameShots[i].getScore())
            self.scoreIndByFrame.append(shotInd)
            self.scores.extend(shotScores)

            # If last frame was just added, compute the total score and set as complete.
            if self.nFrames == BowlingData.nFramesPerRound:
                self.totalScore = self.getTotalScore()
                self.complete = True

    def getTotalScore(self):
        # Computes the total score from the frame data.
        # Requires the data set to be complete or else returns 0.
        #---------------------------------------------------------------------------------#

        # Score the input. Scoring scheme for each frame depends on the frame as well as whether it was
        # a strike, spare, or open frame.
        totalScore = 0
        if self.complete:
            # Computes on completed data set only.
            for iFrame in range(1,self.nFrames+1):
                frameScore = 0
                # Frames 1 through 9 and frame 10 scored differently
                if iFrame <= 10:
                    # Frames 1 through 9
                    # If strike or a spare, add this shot plus next two shots to score.
                    # This is equivalent to how a strike is scored and a spare is scored.
                    shotIndex = self.scoreIndByFrame[iFrame - 1][0]
                    if self.scores[shotIndex] == 10 or (self.scores[shotIndex] + self.scores[shotIndex+1]) == 10:
                        # strike or spare
                        frameScore = sum(self.scores[shotIndex:shotIndex+3])
                    else:
                        # Open - add the 2 shots from the frame
                        frameScore = sum(self.scores[shotIndex:shotIndex+2])
                totalScore += frameScore
        else:
            # Returns zero on incomplete data set.
            totalScore = 0

        return totalScore

    def writeScoresToFile(self,fout):
        # Writes the scores to the file opened by fout. It will fail if fout is closed or read only or invalid.
        # If the data set is not complete, writes out what data is available.
        # Parameters:
        # fout - file object for the output
        #
        # Output will look something like:
        # Frame 01:  3  3
        # Frame 02:  1  4
        # Frame 03:  1  1
        # Frame 04:  1  1
        # Frame 05:  0  2
        # Frame 06: 10
        # Frame 07:  7  3
        # Frame 08:  4  3
        # Frame 09:  1  3
        # Frame 10:  0  0
        # Score: 62
        #

        for i in range(self.nFrames):
            frameScoresStr = "" # string that will have the scores from the shots on each frame
            frameShots = self.frames[i].getShots()
            scoreType = self.frames[i].getScoreType()
            for j in range(len(frameShots)):
                shotScore = frameShots[j].getScore()
                frameScoresStr += str("{0:2d}".format(shotScore) + " ")
            frameScoresStr = frameScoresStr[:-1] # Clips the last space character
            if scoreType == "strike":
                frameScoresStr += "   "
            fout.write("Frame {0:02d}: ".format(i+1) + frameScoresStr + "   " + scoreType + "\n")
        fout.write("Total Score: " + str(self.getTotalScore()))

    def printScores(self):
        # Outputs the scores to the console.
        # If the data set is not complete, writes out what data is available.
        # Parameters:
        # fout - file object for the output
        #
        # Output will look something like:
        # Frame 01:  3  3
        # Frame 02:  1  4
        # Frame 03:  1  1
        # Frame 04:  1  1
        # Frame 05:  0  2
        # Frame 06: 10
        # Frame 07:  7  3
        # Frame 08:  4  3
        # Frame 09:  1  3
        # Frame 10:  0  0
        # Score: 62
        #

        for i in range(self.nFrames):
            frameScoresStr = "" # string that will have the scores from the shots on each frame
            frameShots = self.frames[i].getShots()
            scoreType = self.frames[i].getScoreType()
            for j in range(len(frameShots)):
                shotScore = frameShots[j].getScore()
                frameScoresStr += str("{0:2d}".format(shotScore) + " ")
            frameScoresStr = frameScoresStr[:-1] # Clips the last space character
            if scoreType == "strike":
                frameScoresStr += "   "
            print("Frame {0:02d}: ".format(i+1) + frameScoresStr + "   " + scoreType)
        print("Total Score: " + str(self.getTotalScore()))

        def isComplete(self):
            return self.complete

        def getNumberOfFrames(self):
            return self.nFrames

    def loadScores(self,pkd):
        # Takes in a nested list that has scores for each frame. The list must be
        # BowlingData.nFramesPerRound in length. Each element should be a list with the
        # scores for each frame. [[3,4],[9,1],[10],...]
        # By loading the data this way, all frames are added at once and do not
        # require user input. This is useful for testing, but could be useful for scoring
        # data in general.
        #
        # Could not think of a way to do this without directly
        # accessing the attributes of frame and shot objects
        nFrames = BowlingData.nFramesPerRound
        self.scoreIndByFrame = [[] for i in range(nFrames)]
        self.scores = []
        shotIndex = 0
        # iterate through all the shot scores frame by frame
        for i in range(len(pkd)):
            for j in range(len(pkd[i])):
                self.scoreIndByFrame[i].append(shotIndex)
                self.scores.append(pkd[i][j])
                shotIndex += 1
        self.nFrames = nFrames
        self.complete = True
        self.totalScore = self.getTotalScore()
        for i in range(nFrames):
            shotScores = pkd[i]
            frameNumber = i+1;
            nShots = len(shotScores)
            if nShots == 1:
                scoreType = "strike"
            elif nShots == 2 and sum(shotScores) == 10:
                scoreType = "spare"
            else:
                scoreType = "open"

            shots = []
            for j in range(nShots):
                shotNumber = j+1;
                pinsStandingBeforeShot = 10-shotScores[j]
                shot = BowlingShot(frameNumber,shotNumber,pinsStandingBeforeShot)
                shot.score = shotScores[j]
                shot.complete = True
                shots.append(shot)

            if i == nFrames-1:
                # Last frame
                frame = LastFrame(frameNumber)
            else:
                frame = RegularFrame(frameNumber)

            frame.scoreType = scoreType
            frame.shots = shots
            frame.nShots = nShots
            frame.complete = True

            self.frames.append(frame)

# This class represents a bowling shot. It contains a score attribute which is initially zero, and
# a method to retrieve user input to record the score for the shot.
class BowlingShot:
    def __init__(self, frameNumber, shotNumber, pinsStandingBeforeShot) -> None:
        self.nFrames = BowlingData.nFramesPerRound # number of frames in round
        self.frameNumber = frameNumber # which frame, 1 to nFrames
        self.shotNumber = shotNumber # which shot, 1 to 3
        self.pinsStandingBeforeShot = pinsStandingBeforeShot # the number of pins standing when shot taken
        self.score = 0 # the number of pins knocked down after shot taken
        self.complete = False

        # Guards on attributes
        if frameNumber < 1 or frameNumber > self.nFrames:
            raise Exception("Error: BowlingShot->getUserInput() 'frameNumber' out of bounds.")
        if shotNumber < 1 or shotNumber > 3:
            raise Exception("Error: BowlingShot->getUserInput() 'shot' out of bounds.")
        if pinsStandingBeforeShot < 0 or pinsStandingBeforeShot > 10:
            raise Exception("Error: BowlingShot->getUserInput() 'pinsStandingBeforeShot' out of bounds")

    def setScore(self,score):
        self.score = score
        self.complete = True

    def getScore(self):
        if self.complete:
            return self.score
        else:
            raise Exception("Error: BowlingShot->getScore - data for shot not recorded yet.")

    def getScoreFromUser(self):
        # Prompts the user for the number of pins kncocked down on a particular frame and shot.
        # Returns an integer value representing the number of pins knocked down as entered by the user.
        # Parameters:
        # nFrames - number of frames
        # frame - which frame, 1 to nFrames
        # shot - which shot, 1 to 3
        # maxPins - restricts the maximum pins to enter as knocked down. Takes a value between 0 and 10.

        # Prompts until a valid input is entered. Range is 0 to pinsStandingBeforeShot pins.
        validInput = False
        x = ""
        while not validInput:
            x = input("Frame " + str(self.frameNumber) + " Shot " + str(self.shotNumber) + ": ")
            if x == "q":
                raise Exception("Error: User abort.")
            else:
                # input should be an integer value within bounds
                try:
                    if int(x) >= 0 and int(x) <= self.pinsStandingBeforeShot:
                        validInput = True
                    else:
                        print("Invalid input, please try again.")
                except ValueError:
                        print("Invalid input, please try again.")
        self.score = int(x)
        self.complete = True

    def isComplete(self):
        return self.complete

# This class represents a bowling frame. It is a parent class for two derived classes, RegularFrame and LastFrame.
# It is a container for bowling shots.
class BowlingFrame:
    def __init__(self,frameNumber) -> None:
        self.frameNumber = frameNumber
        self.scoreType = "" # Open, Spare, or Strike as defined in requirements. For tenth frame, Strike takes precedence over spare.
        self.shots = [] # List of BowlingShots in order of occurence for this frame.
        self.nShots = 0
        self.complete = False # when the frame is complete this is true. All the scores for the shots taken on the frame have been collected.

    def addShot(self):
        pass

    def getShots(self):
        return self.shots

    def getNumberOfShots(self):
        return self.nShots

    def isComplete(self):
        return self.complete

    def getScoreType(self):
        return self.scoreType

# This class represents frames in which there are two shots possible.
class RegularFrame(BowlingFrame):
    def __init__(self, frameNumber) -> None:
        super().__init__(frameNumber)

    def addShot(self):
        # Creates the shot, gets user to score the shot, and adds it to the list of shots.
        # If the first shot is a strike, it marks it as complete with one shot.
        # If the first shot is not a strike, then it remains incomplete and open for another shot.
        # If the second shot gets the rest of the pins, it is a spare, otherwise open.
        # Upon the second shot, it is marked as complete.
        # The user is given feedback about the type of the frame when it is determined, open, strike, or spare.
        if self.complete:
            print("Cannot add a shot to this frame. This frame is complete.")
        else:
            if self.nShots == 0:
                # no shots have been taken yet this frame
                pinsStandingBeforeShot = 10
                self.nShots += 1
                shot = BowlingShot(self.frameNumber, self.nShots, pinsStandingBeforeShot)
                shot.getScoreFromUser()
                self.shots.append(shot)
                if shot.getScore() == 10:
                    print("Strike!")
                    self.scoreType = "strike"
                    self.complete = True
            elif self.nShots == 1:
                # one shot taken already
                pinsStandingBeforeShot = 10-self.shots[0].getScore()
                self.nShots += 1
                shot = BowlingShot(self.frameNumber, self.nShots, pinsStandingBeforeShot)
                shot.getScoreFromUser()
                self.shots.append(shot)
                if self.shots[0].getScore() + self.shots[1].getScore() == 10:
                    print("Spare!")
                    self.scoreType = "spare"
                else:
                    print("Open.")
                    self.scoreType = "open"
                self.complete = True

# This class represents the last frame in which there are three shots possible.
class LastFrame(BowlingFrame):
    def __init__(self, frameNumber) -> None:
        super().__init__(frameNumber)

    def addShot(self):
        # Creates the shot, gets user to score the shot, and adds it to the list of shots.
        # If the first shot is a strike, it marks it as incomplete with two shots following, and pins are reset.
        # If the first shot is not a strike, then it remains incomplete and open for another shot for a possible spare.
        # If the second shot gets the rest of the pins for a spare, or a second strike, it is reset for another shot.
        # If neither a spare or strike occurs on the first two shots, the frame is complete and marked as open.
        # If there is a third shot, whatever the outcome, the frame is marked as complete.
        # The user is given feedback about the type of the frame when it is determined, open, strike, or spare.
        if self.complete:
            print("Cannot add a shot to this frame. This frame is complete.")
        else:
            if self.nShots == 0:
                # no shots have been taken yet this frame
                pinsStandingBeforeShot = 10
                self.nShots += 1
                shot = BowlingShot(self.frameNumber, self.nShots, pinsStandingBeforeShot)
                shot.getScoreFromUser()
                self.shots.append(shot)
                if shot.getScore() == 10:
                    print("Strike!")
                    self.scoreType = "strike"
            elif self.nShots == 1:
                # one shot taken already
                # if first shot was a strike, pins reset.
                if self.shots[0].getScore() == 10:
                    pinsStandingBeforeShot = 10
                else:
                    pinsStandingBeforeShot = 10-self.shots[0].getScore()
                self.nShots += 1
                shot = BowlingShot(self.frameNumber, self.nShots, pinsStandingBeforeShot)
                shot.getScoreFromUser()
                self.shots.append(shot)
                if shot.getScore() == 10:
                    print("Strike!")
                elif self.shots[0].getScore() + self.shots[1].getScore() == 10:
                    print("Spare!")
                    self.scoreType = "spare"
                elif self.shots[0].getScore() + self.shots[1].getScore() < 10:
                    print("Open.")
                    self.scoreType = "open"
                    self.complete = True
            elif self.nShots == 2:
                # two shots taken already
                # this means a strike or spare must have been scored
                # and so the pins must be reset
                pinsStandingBeforeShot = 10
                self.nShots += 1
                shot = BowlingShot(self.frameNumber, self.nShots, pinsStandingBeforeShot)
                shot.getScoreFromUser()
                self.shots.append(shot)
                if shot.getScore() == 10:
                    print("Strike!")
                else:
                    print("Open.")

                self.complete = True


from utils.request import request_telgram, request_pwrtelgram
import json

def check_querys(chat_id=None, text=None, parse_mode=None, disable_web_page_preview=None,
				disable_notification=None, reply_to_message_id=None, reply_markup=None,
				inline_keyboard=None, photo=None, caption=None, audio=None, duration=None,
				performer=None, title=None, document=None, sticker=None, video=None,
				width=None,height=None, voice=None, latitude=None, longitude=None,
				address=None, foursquare_id=None, phone_number=None,
				first_name=None, last_name=None, action=None, user_id=None, offset=None,
				limit=None, file__id=None, callback_querys_id=None, show_alert=None,
				inline_message_id=None, message_id=None, timeout=None, from_chat_id=None,
				inline_query_id=None, results=None, cache_time=None, is_personal=None,
				next_offset=None, switch_pm_text=None, switch_pm_parameter=None, file=None,
				name=None):

	querys = {}
	file_ = {}
	if chat_id:
		querys['chat_id'] = chat_id
	if text:
		querys['text'] = text
	if parse_mode:
		querys['parse_mode'] = parse_mode
	if disable_web_page_preview:
		querys['disable_web_page_preview'] = disable_web_page_preview
	if disable_notification:
		querys['disable_notification'] = disable_notification
	if reply_to_message_id:
		querys['reply_to_message_id'] = reply_to_message_id
	if reply_markup:
		querys['reply_markup'] = reply_markup
	if inline_keyboard:
		querys['reply_markup'] = '{"inline_keyboard":' + inline_keyboard + '}'
	if from_chat_id:
		querys['from_chat_id'] = from_chat_id
	if photo:
		file_['photo'] = photo
	if caption:
		querys['caption'] = caption
	if audio:
		file_['audio'] = audio
	if duration:
		querys['duration'] = duration
	if performer:
		querys['performer'] = performer
	if title:
		querys['title'] = title
	if document:
		file_['document'] = document
	if sticker:
		file_['sticker'] = sticker
	if video:
		file_['video'] = video
	if width:
		querys['width'] = width
	if height:
		querys['height'] = height
	if voice:
		file_['voice'] = voice
	if latitude:
		querys['latitude'] = latitude
	if longitude:
		querys['longitude'] = longitude
	if address:
		querys['address'] = address
	if foursquare_id:
		querys['foursquare_id'] = foursquare_id
	if phone_number:
		querys['phone_number'] = phone_number
	if first_name:
		querys['first_name'] = first_name
	if last_name:
		querys['last_name'] = last_name
	if action:
		querys['action'] = action
	if user_id:
		querys['user_id'] = user_id
	if offset:
		querys['offset'] = offset
	if limit:
		querys['limit'] = limit
	if file__id:
		querys['file__id'] = file__id
	if callback_querys_id:
		querys['callback_querys_id'] = callback_querys_id
	if show_alert:
		querys['show_alert'] = show_alert
	if inline_message_id:
		querys['inline_message_id'] = inline_message_id
	if message_id:
		querys['message_id'] = message_id
	if timeout:
		querys['timeout'] = timeout
	if name:
		querys['name'] = name
	if file:
		querys['file'] = file
	#Inline
	if inline_query_id:
		querys['inline_query_id'] = inline_query_id
	if results:
		querys['results'] = results
	if cache_time:
		querys['cache_time'] = cache_time
	if is_personal:
		querys['is_personal'] = is_personal
	if next_offset:
		querys['next_offset'] = next_offset
	if switch_pm_text:
		querys['switch_pm_text'] = switch_pm_text
	if switch_pm_parameter:
		querys['switch_pm_parameter'] = switch_pm_parameter
	return querys, file_

#https://core.telegram.org/bots/api/#available-methods
def getMe():
	return request_telgram('getMe')

def getUpdates(offset=None, limit=None, timeout=None):
	querys, file_ = check_querys(offset=offset,
							limit=limit,
							timeout=timeout)
	return request_telgram('getUpdates', querys, file_)

def sendMessage(chat_id=None, text=None, parse_mode=None, disable_web_page_preview=None,
				disable_notification=None, reply_to_message_id=None, reply_markup=None,
				inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							text=text,
							parse_mode=parse_mode,
							disable_web_page_preview=disable_web_page_preview,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendMessage', querys, file_)

def forwardMessage(chat_id=None, from_chat_id=None, disable_notification=None,
					message_id=None):

	querys, file_ = check_querys(chat_id=chat_id,
							from_chat_id=from_chat_id,
							disable_notification=disable_notification,
							message_id=message_id)
	return request_telgram('forwardMessage', querys, file_)

def sendPhoto(chat_id=None, photo=None, caption=None, disable_notification=None,
				reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							photo=photo,
							caption=caption,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendPhoto', querys, file_)

def sendAudio(chat_id=None, audio=None, duration=None, performer=None, title=None,
			disable_notification=None, reply_to_message_id=None, reply_markup=None,
			inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							audio=audio,
							duration=duration,
							performer=performer,
							title=title,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendAudio', querys, file_)

def sendDocument(chat_id=None, document=None, caption=None, disable_notification=None,
				reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							document=document,
							caption=caption,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendDocument', querys, file_)

def sendSticker(chat_id=None, sticker=None, disable_notification=None, reply_to_message_id=None,
				reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							sticker=sticker,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendSticker', querys, file_)

def sendVideo(chat_id=None, video=None, duration=None, width=None, height=None,
				caption=None, disable_notification=None, reply_to_message_id=None,
				reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							video=video,
							duration=duration,
							width=width,
							height=height,
							caption=caption,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendVideo', querys, file_)

def sendVoice(chat_id=None, voice=None, duration=None, disable_notification=None,
				reply_to_message_id=None, reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							voice=voice,
							duration=duration,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendVoice', querys, file_)

def sendLocation(chat_id=None, latitude=None, longitude=None, disable_notification=None,
				reply_to_message_id=None, reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							latitude=latitude,
							longitude=longitude,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendLocation', querys, file_)

def sendVenue(chat_id=None, latitude=None, longitude=None, title=None, address=None,
				foursquare_id=None, disable_notification=None, reply_markup=None,
				inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							latitude=latitude,
							longitude=longitude,
							title=title,
							address=address,
							foursquare_id=foursquare_id,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendVenue', querys, file_)

def sendContact(chat_id=None, phone_number=None, first_name=None, last_name=None,
				disable_notification=None, reply_to_message_id=None, reply_markup=None,
				inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							phone_number=phone_number,
							first_name=first_name,
							last_name=last_name,
							disable_notification=disable_notification,
							reply_to_message_id=reply_to_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('sendContact', querys, file_)

def sendChatAction(chat_id=None, action=None):

	querys, file_ = check_querys(chat_id=chat_id,
							action=action)
	return request_telgram('sendChatAction', querys, file_)

def sendFile(chat_id=None, file=None, name=None):

	querys, file_ = check_querys(chat_id=chat_id,
							file=file,
							name=name)
	return request_pwrtelgram('sendFile', querys, file_)

def getUserProfile_Photos(user_id=None, offset=None, limit=None):

	querys, file_ = check_querys(user_id=user_id,
							offset=offset,
							limit=limit)
	return request_telgram('getUserProfile_Photos', querys, file_)

def getfile_(file__id=None):

	querys, file_ = check_querys(file__id=file__id)
	return request_telgram('getfile_', querys, file_)

def kickChatMember(chat_id=None, user_id=None):

	querys, file_ = check_querys(chat_id=chat_id,
							user_id=user_id)
	return request_telgram('kickChatMember', querys, file_)

def leaveChat(chat_id=None):

	querys, file_ = check_querys(chat_id=chat_id)
	return request_telgram('leaveChat', querys, file_)

def unbanChatMember(chat_id=None, user_id=None):

	querys, file_ = check_querys(chat_id=chat_id,
							user_id=user_id)
	return request_telgram('unbanChatMember', querys, file_)

def getChat(chat_id=None):

	querys, file_ = check_querys(chat_id=chat_id)
	return request_telgram('getChat', querys, file_)

def getChatAdministrators(chat_id=None):

	querys, file_ = check_querys(chat_id=chat_id)
	return request_telgram('getChatAdministrators', querys, file_)

def getChatMembersCount(chat_id=None):

	querys, file_ = check_querys(chat_id=chat_id)
	return request_telgram('getChatMembersCount', querys, file_)

def getChatMember(chat_id=None, user_id=None):

	querys, file_ = check_querys(chat_id=chat_id,
							user_id=user_id)
	return request_telgram('getChatMember', querys, file_)

def answerInlinequerys(callback_querys_id=None, text=None, show_alert=None):

	querys, file_ = check_querys(callback_querys_id=callback_querys_id,
							text=text,
							show_alert=show_alert)
	return request_telgram('answerInlinequerys', querys, file_)

#https://core.telegram.org/bots/api/#updating-messages
def editMessageText(chat_id=None, message_id=None, inline_message_id=None, text=None,
				parse_mode=None, disable_web_page_preview=None, reply_markup=None,
				inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							message_id=message_id,
							inline_message_id=inline_message_id,
							text=text,
							parse_mode=parse_mode,
							disable_web_page_preview=disable_web_page_preview,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('editMessageText', querys, file_)

def editMessageCaption(chat_id=None, message_id=None, inline_message_id=None, caption=None,
				reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							message_id=message_id,
							inline_message_id=inline_message_id,
							caption=caption,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('editMessageCaption', querys, file_)

def editMessageReplyMarkup(chat_id=None, message_id=None, inline_message_id=None,
				reply_markup=None, inline_keyboard=None):

	querys, file_ = check_querys(chat_id=chat_id,
							message_id=message_id,
							inline_message_id=inline_message_id,
							reply_markup=reply_markup,
							inline_keyboard=inline_keyboard)
	return request_telgram('editMessageReplyMarkup', querys, file_)

#https://core.telegram.org/bots/api/#inline-mode
def sendInline(inline_query_id=None, results=None, cache_time=None, is_personal=None,
				next_offset=None, switch_pm_text=None, switch_pm_parameter=None):

	querys, file_ = check_querys(inline_query_id=inline_query_id,
							results=results,
							cache_time=cache_time,
							is_personal=is_personal,
							next_offset=next_offset,
							switch_pm_text=switch_pm_text,
							switch_pm_parameter=switch_pm_parameter)
	return request_telgram('answerInlineQuery', querys, file_)


from sqlite3 import ProgrammingError, IntegrityError

from db import connection, DATA_AB_NYC_DB
import logging

from exceptions.db_exceptions import InvalidData

logger = logging.getLogger(__name__)

def insert_all_data(all_data: dict, db_file=DATA_AB_NYC_DB):
    """
    Insert data into given db file if db file is not exist
    :param all_data: all data as dictionary
    :param db_file: db file
    :return: return True if insertion successful
    """
    insert_room_types(all_data["room_types"], db_file)
    insert_neighbourhood_groups(all_data["neighbourhood_groups"], db_file)
    insert_neighbourhoods(all_data["neighbourhoods"], db_file)
    insert_ab_data(all_data["ab_data"], db_file)

def insert_room_types(room_types: list, db_file):
    """
    Insert room type data
    :param room_types: room types as list of tuples
    :param db_file: database file
    :raise InvalidData when data is invalid
    :return:
    """
    try:
        insert_to_table(room_types, 'INSERT INTO room_types VALUES (?,?)', db_file)
    except ProgrammingError as e:
        raise InvalidData("Provided data is invalid " + str(e))
    except IntegrityError as e:
        raise InvalidData("Duplicate ID " + str(e))

def insert_neighbourhood_groups(neighbourhood_groups: list, db_file):
    """
    Insert neighbourhood groups type data
    :param neighbourhood_groups: neighbourhood groups as list of tuples
    :param db_file: database file
    :raise InvalidData when data is invalid
    :return:
    """
    try:
        insert_to_table(neighbourhood_groups, 'INSERT INTO neighbourhood_groups VALUES (?,?)', db_file)
    except ProgrammingError as e:
        raise InvalidData("Provided data is invalid " + str(e))
    except IntegrityError as e:
        raise InvalidData("Duplicate ID " + str(e))

def insert_neighbourhoods(neighbourhoods: list, db_file):
    """
    Insert neighbourhood  type data
    :param neighbourhoods: neighbourhood as list of tuples
    :param db_file: database file
    :raise InvalidData when data is invalid
    :return:
    """
    try:
        insert_to_table(neighbourhoods, 'INSERT INTO neighbourhoods VALUES (?,?)', db_file)
    except ProgrammingError as e:
        raise InvalidData("Provided data is invalid " + str(e))
    except IntegrityError as e:
        raise InvalidData("Duplicate ID " + str(e))

def insert_ab_data(ab_data: list, db_file):
    """
    Insert main airbnb data
    :param ab_data: airbnb data as list of tuples
    :param db_file: database file
    :raise InvalidData when data is invalid
    :return:
    """
    try:
        insert_to_table(ab_data, 'INSERT INTO ab_data VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', db_file)
    except ProgrammingError as e:
        raise InvalidData("Provided data is invalid " + str(e))

def insert_to_table(data_list, insert_command, db_file):
    """
    Insert data into a given table
    :param data_list: data as list of tuples
    :param insert_command: insert command
    :param db_file: database file
    :return:
    """
    con = connection.connect(db_file)
    cur = con.cursor()
    cur.executemany(insert_command, data_list)
    con.commit()
    connection.close(con)


from src.tagger import LstmNER

from argparse import ArgumentParser

if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument("-m", "--model_path", default="./models/lstm-ner")
    args = parser.parse_args()

    ner = LstmNER.from_pretrained(args.model_path)

    text = "I Love Anna Marry"

    output = ner.run(text=text)

    print(output)


N, K = map(int, input().split())

A = 1/(2*N)

tmp = 2**N
B = (tmp-1)/tmp

print(A*B)
######################
N, K = map(int, input().split())

ans = 0
for i in range(1, N + 1):
    p = 1
    while i < K:
        p *= 0.5
        i *= 2
    ans += p / N

print(ans)

Atcoder/20200831_くじかつ/C - Dice and Coin.py


import os
import sys
## import datetime

try:
    gFileDir = os.path.dirname(os.path.abspath(__file__))
except:
    gFileDir = os.path.dirname(os.path.abspath(sys.argv[0]))
gImgDir = gFileDir + os.sep + 'images'
gPyKritaDir = os.path.dirname(gFileDir)
gKritaDir = os.path.dirname(gPyKritaDir)
gPicsDir = gKritaDir + os.sep + 'pics'
gShareDir = os.path.dirname(gKritaDir)

#--PySide/PyQt Imports. https://pypi.org/project/PyQt5/
from PyQt5.QtGui import  QBitmap, QBrush, QColor, QCursor, QImage, QLinearGradient, QPainter, QPen, QPixmap
from PyQt5.QtCore import Qt, QEvent, QPoint, QRect, QSize
from PyQt5.QtWidgets import QAbstractButton, QAction, QApplication, QDialog, QMenu, QMessageBox, QWidget, QTextEdit

#--Krita Imports -- https://krita.org/
try:
    import krita
except ImportError:
    krita = None
    print('FAILED to import krita')

## def get_modification_date(filename):
##     timestamp = os.path.getmtime(filename)
##     return datetime.datetime.fromtimestamp(timestamp)

class ImageButton(QAbstractButton):
    def __init__(self, image, image_hover, image_pressed, parent=None):
        super(ImageButton, self).__init__(parent)
        self.image = image
        self.image_hover = image_hover
        self.image_pressed = image_pressed

        self.pressed.connect(self.update)
        self.released.connect(self.update)

    def paintEvent(self, event):
        img = self.image_hover if self.underMouse() else self.image
        if self.isDown():
            img = self.image_pressed

        painter = QPainter(self)
        painter.drawImage(event.rect(), img)

    def enterEvent(self, event):
        self.update()
        # QApplication.setOverrideCursor(QCursor(Qt.ArrowCursor))
        QApplication.setOverrideCursor(QCursor(QPixmap(gImgDir + os.sep + 'paperairplane_arrow_white24.png'), hotX=0, hotY=0))

    def leaveEvent(self, event):
        self.update()
        QApplication.restoreOverrideCursor()

    def sizeHint(self):
        return QSize(self.image.width(), self.image.height())

class PopupPaletteFrame(QDialog):
    def __init__(self, parent=None, f=Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint):
        super(PopupPaletteFrame, self).__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_NoSystemBackground, True)
        self.setAttribute(Qt.WA_TranslucentBackground, True)

        self.setGeometry(0, 0, 512, 512)

        self.offset = QPoint(0, 0)

        self.local_image = QImage(512, 512, QImage.Format_ARGB32)
        self.fgcolor = '#FFFFFF'
        self.bgcolor = '#000000'

        # This is how to load the merged image from a .kra file.
        ## import zipfile
        ## archive = zipfile.ZipFile(gImgDir + os.sep + 'myPopupPaletteWorkshop.kra', 'r')
        ## img_data = archive.read('mergedimage.png')
        ## img = QImage()
        ## img.loadFromData(img_data)
        ## self.texture = img

        self.texture = QImage(gImgDir + os.sep + 'colorwheel_pixel_palette.png')
        self.defaultblackwhiteImg = QImage(gImgDir + os.sep + 'defaultblackwhite10.png')
        self.switcharrowsImg = QImage(gImgDir + os.sep + 'switcharrows16.png')
        self.colorboxImg = QImage(gImgDir + os.sep + 'colorbox28.png')

        lHiColorDir = gShareDir + os.sep + 'icons' + os.sep + 'hicolor'

        # Text box.
        self.textbox = QTextEdit("Test QTextEdit", parent=self)
        self.textbox.move(192, 160)
        self.textbox.setGeometry(192, 160, 128, 32)

        # Big Button in middle.
        self.btnImage = btnImage = QImage(lHiColorDir + os.sep + '128x128' + os.sep + 'apps' + os.sep + 'calligrakrita.png')
        self.button = ImageButton(btnImage, btnImage, btnImage, self)
        self.button.move(256 - 64, 256 - 64)
        self.button.pressed.connect(self.buttonPressed)
        self.button.released.connect(self.buttonReleased)
        self.button.setToolTip('Right Click for Context Menu')
        self.button.setContextMenuPolicy(Qt.CustomContextMenu)
        self.button.customContextMenuRequested.connect(self.show_context_menu)

        # Create popup context menu.
        self._context_menu = QMenu(self)
        self._context_menu.addAction('Invert', self.invertTexture)
        self._context_menu.addAction('Mirror Horizontal', self.mirrorTextureHorizontal)
        self._context_menu.addAction('Mirror Vertical', self.mirrorTextureVertical)

        # 4 smaller buttons.
        self.btnImage1 = btnImage1 = QImage(lHiColorDir + os.sep + '32x32' + os.sep + 'apps' + os.sep + 'calligrakrita.png')
        self.button1 = ImageButton(btnImage1, btnImage1, btnImage1, self)
        self.button1.move(192, 320)
        self.button1.released.connect(self.button1Released)
        self.button1.setToolTip('test button')

        self.btnImage2 = btnImage2 = QImage(lHiColorDir + os.sep + '32x32' + os.sep + 'apps' + os.sep + 'calligrakrita.png')
        self.button2 = ImageButton(btnImage2, btnImage2, btnImage2, self)
        self.button2.move(224, 320)
        self.button2.released.connect(self.button2Released)
        self.button2.setToolTip('rotate_canvas_left')

        self.btnImage3 = btnImage3 = QImage(lHiColorDir + os.sep + '32x32' + os.sep + 'apps' + os.sep + 'calligrakrita.png')
        self.button3 = ImageButton(btnImage3, btnImage3, btnImage3, self)
        self.button3.move(256, 320)
        self.button3.released.connect(self.button3Released)
        self.button3.setToolTip('rotate_canvas_right')

        self.btnImage4 = btnImage4 = QImage(lHiColorDir + os.sep + '32x32' + os.sep + 'apps' + os.sep + 'calligrakrita.png')
        self.button4 = ImageButton(btnImage4, btnImage4, btnImage4, self)
        self.button4.move(288, 320)
        self.button4.released.connect(self.button4Released)
        self.button4.setToolTip('Im a painter button')

        # Fore/Back color stuff
        self.arrowsBtn = ImageButton(self.switcharrowsImg, self.switcharrowsImg, self.switcharrowsImg, parent=self)
        self.arrowsBtn.move(464, 453)
        self.arrowsBtn.pressed.connect(self.swapFG_BG_Colors)

        self.defblackwhiteBtn = ImageButton(self.defaultblackwhiteImg, self.defaultblackwhiteImg, self.defaultblackwhiteImg, parent=self)
        self.defblackwhiteBtn.move(436, 486)
        self.defblackwhiteBtn.pressed.connect(self.defaultFG_BG_Colors)

        if not krita:
            quitAction = QAction("E&xit", self, shortcut="Z",
                                 triggered=self.close)
            self.button.addAction(quitAction)
            self.button.setContextMenuPolicy(Qt.ActionsContextMenu)

        # Mask - Anything not black (0, 0, 0, 0-255) in this image will not be shown when drawn out.
        self.setMask(QBitmap(gImgDir + os.sep + "pixel_palette_mask512.png"))
        self.drawGUI()

        self.setFocusPolicy(Qt.StrongFocus)
        self.setFocus()

        self.setMouseTracking(True)

    def __del__(self):
        print('__del__ %s' % self.__class__.__name__)

    def mirrorTextureHorizontal(self):
        self.texture = self.texture.mirrored(horizontal=True, vertical=False)
        self.drawGUI()
        self.update()

    def mirrorTextureVertical(self):
        self.texture = self.texture.mirrored(horizontal=False, vertical=True)
        self.drawGUI()
        self.update()

    def invertTexture(self):
        self.texture.invertPixels()
        self.drawGUI()
        self.update()

    def swapFG_BG_Colors(self):
        self.fgcolor, self.bgcolor = self.bgcolor, self.fgcolor
        self.drawGUI()
        self.update()

    def defaultFG_BG_Colors(self):
        self.fgcolor = '#FFFFFF'
        self.bgcolor = '#000000'
        self.drawGUI()
        self.update()

    def show_context_menu(self, point):
        self._context_menu.exec_(self.button.mapToGlobal(point))

    def buttonPressed(self):
        pass

    def buttonReleased(self):
        QApplication.beep()

    def button1Released(self):
        QMessageBox.information(QWidget(self), i18n("Test Btn1"),
                                i18n("Hello! This is Krita version %s\n\nwidth %s height %s") % (Application.version(), self.width(), self.height()))

    def button2Released(self):
        krita.Krita.instance().action('rotate_canvas_left').trigger()

    def button3Released(self):
        krita.Krita.instance().action('rotate_canvas_right').trigger()

    def button4Released(self):
        QMessageBox.information(QWidget(self), i18n("Test Btn4"),
                                i18n('"The position of the artist is humble. He is essentially a channel." Piet Mondrian'))

    def saveData(self):
        with open(gFileDir + os.sep + 'test.txt', 'w') as f:
            f.write('I Closed Cleanly and wrote out some data')

    def closeEvent(self, event):
        self.saveData()
        event.accept()

    def sizeHint(self):
        return QSize(512, 512)

    def drawGUI(self):
        # Draw everything to an image first so we can have a local copy
        # available for pixel RGBA access.
        qimage = QImage(QSize(512, 512), QImage.Format_ARGB32)
        qp = QPainter(qimage)
        qp.setRenderHint(QPainter.Antialiasing)
        qp.setPen(Qt.NoPen)

        # Draw the texture.
        qp.drawImage(0, 0, self.texture)

        # Draw the graydient bar
        myQRect = QRect(497, 1, 14, 510)
        gradient = QLinearGradient(0, 0, 0, 512)
        gradient.setColorAt(0, QColor(self.fgcolor))
        gradient.setColorAt(1, QColor(self.bgcolor))
        qp.fillRect(myQRect, gradient)
        qp.setPen(QPen(QColor(0, 0, 0, 255), 1))
        qp.setBrush(QBrush(QColor(0, 0, 0, 0)))
        qp.drawRect(497, 1, 14, 510)

        # Draw the fore/back color boxes
        qp.setPen(Qt.NoPen)
        qp.drawImage(464, 453, self.switcharrowsImg)
        qp.drawImage(436, 486, self.defaultblackwhiteImg)
        qp.setBrush(QColor(self.bgcolor))
        qp.drawRect(452, 468, 28, 28)
        qp.drawImage(452, 468, self.colorboxImg)
        qp.setBrush(QColor(self.fgcolor))
        qp.drawRect(436, 452, 28, 28)
        qp.drawImage(436, 452, self.colorboxImg)

        # Save a copy of the finished image locally.
        self.local_image = qimage

    def paintEvent(self, event):
        # Draw the completed image to the widget/window frame.
        qp = QPainter()
        qp.begin(self)
        qp.drawImage(0, 0, self.local_image)
        qp.end()

    def updateKritaForeGroundColor(self, color):
        instance = krita.Krita.instance()
        views = instance.views()
        view0 = views[0]

        managedColor = krita.ManagedColor("RGBA", "U8", "")
        colorComponents = managedColor.components()
        colorComponents[0] = color.blueF()    # b
        colorComponents[1] = color.greenF()   # g
        colorComponents[2] = color.redF()     # r
        colorComponents[3] = color.alphaF()   # a
        managedColor.setComponents(colorComponents)

        view0.setForeGroundColor(managedColor)

    def updateKritaBackGroundColor(self, color):
        instance = krita.Krita.instance()
        views = instance.views()
        view0 = views[0]

        managedColor = krita.ManagedColor("RGBA", "U8", "")
        colorComponents = managedColor.components()
        colorComponents[0] = color.blueF()    # b
        colorComponents[1] = color.greenF()   # g
        colorComponents[2] = color.redF()     # r
        colorComponents[3] = color.alphaF()   # a
        managedColor.setComponents(colorComponents)

        view0.setBackGroundColor(managedColor)

    def mousePressEvent(self, event):
        if event.button() == Qt.MiddleButton:
            self.offset = event.globalPos() - self.frameGeometry().topLeft()
            ## print(self.offset)
        elif event.button() == Qt.LeftButton:
            position = QPoint(event.pos().x(), event.pos().y())
            color = QColor.fromRgb(self.local_image.pixel(position))
            self.fgcolor = color
            ## print(color.redF(), color.greenF(), color.blueF(), color.alphaF())  # print values from a QColor
            if krita:
                self.updateKritaForeGroundColor(color)
            self.drawGUI()
            self.update()  # Refresh the drawn colors.
        elif event.button() == Qt.RightButton:
            position = QPoint(event.pos().x(), event.pos().y())
            color = QColor.fromRgb(self.local_image.pixel(position))
            self.bgcolor = color
            ## print(color.redF(), color.greenF(), color.blueF(), color.alphaF())  # print values from a QColor
            if krita:
                self.updateKritaBackGroundColor(color)
            self.drawGUI()
            self.update()  # Refresh the drawn colors.
        elif event.buttons() == Qt.XButton1:
            QApplication.beep()
            # Do something here... Change a texture/mask/page, call a function, etc....
        elif event.buttons() == Qt.XButton2:
            QApplication.beep()
            # Do something here... Change a texture/mask/page, call a function, etc....

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.MiddleButton:
            self.move(event.globalPos() - self.offset)
            event.accept()
        self.textbox.setPlainText('x = %s, y = %s' % (event.x(), event.y()))

    ## def wheelEvent(self, event):
    ##     event.accept()

    def enterEvent(self, event):
        self.update()
        ## QApplication.setOverrideCursor(QCursor(Qt.ArrowCursor))
        QApplication.setOverrideCursor(QCursor(QPixmap(gImgDir + os.sep + 'paperairplane_arrow_white24.png'), hotX=0, hotY=0))

    def leaveEvent(self, event):
        self.update()
        QApplication.restoreOverrideCursor()

    def event(self, event):
        if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
            ## print("Tab pressed")
            if krita:
                krita.Krita.instance().action('view_show_canvas_only').trigger()
            return False
        return QWidget.event(self, event)

def main():
    frame = PopupPaletteFrame()
    frame.show()
    frame.activateWindow()
    try:
        frame.exec_()
    except Exception as exc:
        raise exc

if __name__ == '__main__':
    app = QApplication([])
    frame = PopupPaletteFrame()
    frame.show()
    app.exec_()


#  This program is free software; you can redistribute it and/or modify6
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
"""ATResearchProject utitility lib"""

## a poor-mans approach to fixing unicode issues :-(
_default_charset = 'utf-8'

# Python stuff
import re, string, types
from types import UnicodeType

def _encode(s, encoding=_default_charset, obj=None):

    if obj and obj.hasProperty('default_charset'):
        encoding=obj.getProperty('default_charset')

    try:
        return s.encode(encoding)
    except (TypeError, UnicodeDecodeError, ValueError):
        return s

def _decode(s, encoding=_default_charset, obj=None, ):

    if obj and obj.hasProperty('default_charset'):
        encoding=obj.getProperty('default_charset')

    try:
        return unicode(s, encoding)
    except (TypeError, UnicodeDecodeError, ValueError):
        return s


import torch
from dgl.data import DGLDataset
from dgl.dataloading import GraphDataLoader
import numpy as np
import dgl

class Dataset(DGLDataset):
    def __init__(self, type=None, ws=None, split=None):
        super().__init__(name='dataset')
        assert type != None, 'type should be in ["straight", "curve"]'
        assert ws != None, 'Error : window size should NOT be None type!'
        assert split in ['train', 'inference'], 'Dataset split error'

        # init variables
        self.type = type
        self.ws = ws
        self.split = split

        # load data
        self.data = np.load('./data/s_data.npy', allow_pickle=True) if type=='straight' else np.load('./data/c_data.npy', allow_pickle=True)
        if split == 'inference':
            self.data = self.data[10001-ws+1:10001-ws+1+1999]

    def __len__(self):
        if self.split == 'train':
            return 10001-self.ws+1
        else: # 'inference'
            return 1999

    def __getitem__(self, idx):
        distance = torch.zeros((self.ws, 1))
        lane = torch.zeros((self.ws, 5))
        norm_target = torch.zeros((self.ws, 5, 4))
        feature_wheel = torch.zeros((4, self.ws, 5, 8))
        feature_sensor = torch.zeros((2, self.ws , 5, 4))
        graph = dgl.heterograph({('wheel', 'front', 'wheel'): ([0,3],[3,0]),
                                 ('wheel', 'left', 'wheel'): ([0,1],[1,0]),
                                 ('wheel', 'right', 'wheel'): ([3,2],[2,3]),
                                 ('wheel', 'rear', 'wheel'): ([1,2],[2,1]),
                                 ('wheel', 'connect', 'sensor'):([0,1,2,3],[0,0,1,1]),
                                 ('sensor', 'connect', 'wheel'):([0,0,1,1],[0,1,2,3])})
        for i in range(self.ws):
            distance[i] = self.data[idx+i]['distance']
            feature_wheel[:,i,...] = self.data[idx+1]['graph'].nodes['wheel'].data['feature']
            feature_sensor[:,i,...] = self.data[idx+1]['graph'].nodes['sensor'].data['feature']
            lane[i] = self.data[idx+i]['lane'] if self.type=='straight' else torch.zeros(5)
            norm_target[i] = self.data[idx+i]['norm_target']
        graph.nodes['wheel'].data['feature'] = feature_wheel.type(torch.float32)
        graph.nodes['sensor'].data['feature'] = feature_sensor.type(torch.float32)
        distance = distance.type(torch.float32)
        lane = lane.type(torch.float32)
        norm_target = norm_target.type(torch.float32)

        target = self.data[idx+self.ws]['target'].type(torch.float32)
        return distance, lane, graph, norm_target, target

def test_dataset(type, ws, split):
    dataset = Dataset(type, ws, split)
    dataloader = GraphDataLoader(dataset, batch_size=32, shuffle=True, drop_last=False)
    distance, lane, graph, norm_target, target = next(iter(dataloader))
    print(f'======= Test on [type : {type}, ws : {ws}, split : {split}] ======')
    print(f'dataset length when ws is {ws} : {len(dataset)}')
    print(f'distance : {distance.shape}')
    print(f'lane : {lane.shape}')
    print(f'norm_target : {norm_target.shape}')
    print(f'target : {target.shape}')
    print(f'graph/wheel : {graph.nodes["wheel"].data["feature"].shape}')
    print(f'graph/sensor : {graph.nodes["sensor"].data["feature"].shape}')
    print('==================================================================\n')

if __name__ == '__main__':
    test_dataset('straight', 10, 'train')
    test_dataset('straight', 10, 'inference')
    test_dataset('curve', 10, 'train')
    test_dataset('curve', 10, 'inference')
    '''
    ======= Test on [type : straight, ws : 10, split : train] ======
    dataset length when ws is 10 : 9992
    distance : torch.Size([32, 10, 1])
    lane : torch.Size([32, 10, 5])
    norm_target : torch.Size([32, 10, 5, 4])
    target : torch.Size([32, 4])
    graph/wheel : torch.Size([128, 10, 5, 8])
    graph/sensor : torch.Size([64, 10, 5, 4])
    ==================================================================
    '''


import logging
import itertools

from flask import g, jsonify
from flask_login import current_user

from app import db, constants
from app.utils import scoped_session
from app.models import create_destination_plate
from app.dbmodels import NGS_BARCODE_PLATE  # , NGS_BARCODE_PLATE_TYPE

from twistdb import create_unique_id
from twistdb.sampletrack import (Sample, Plate, Transform, PlateType,
                                 TransformDetail)

IGNORE_MISSING_SOURCE_PLATE_WELLS = True  # FIXME: this allows silent failures

logger = logging.getLogger(name="Spreadsheet")

def error_response(status_code, message):
    # TODO: remove duplicate code -- use JSON-API?
    response = jsonify({'success': False, 'message': message})
    response.status_code = status_code
    return response

#
# If the user uploaded a spreadsheet with each row representing a well-to-well transform, this is where we
# process that spreadsheet data.
#

def create_step_record_adhoc(transform_type_id,
                             transform_template_id,
                             transform_map):

    with scoped_session(db.engine) as db_session:
        result = create_adhoc_sample_movement(db_session,
                                              transform_type_id,
                                              transform_template_id,
                                              transform_map)
        if result["success"]:
            return jsonify({
                "success": True
            })
        else:
            return error_response(400, result["errorMessage"])

def create_adhoc_sample_movement(db_session,
                                 transform_type_id,
                                 transform_template_id, transform_map,
                                 transform_spec_id=None):
    #
    # FIRST. Create a "sample_transform" row representing this row's transform.
    #
    operator = g.user
    sample_transform = Transform(transform_type_id=transform_type_id,
                                 transform_spec_id=transform_spec_id,
                                 operator_id=operator.operator_id)
    db_session.add(sample_transform)
    db_session.flush()

    import time
    start = time.time()

    # Cache all source plates, raising an error if any are not found
    plate_cache = {}
    srcs = set([d['source_plate_barcode'] for d in transform_map])
    logging.info("Caching source plates: %s" % list(srcs))
    for src_barcode in srcs:
        try:
            source_plate = db_session.query(Plate) \
                                     .filter(Plate.external_barcode == src_barcode).one()
        except:
            logging.warn(" %s encountered error creating sample transform. "
                         "There is no source plate with the barcode: [%s]",
                         g.user.first_and_last_name, src_barcode)
            return {
                "success": False,
                "errorMessage": "There is no source plate with the barcode: [%s]" % (src_barcode)
            }
        else:
            plate_cache[src_barcode] = source_plate

    # Cache all destination plates, creating any that are not found

    dests = set([(d['destination_plate_barcode'],
                  d['destination_plate_type']) for d in transform_map])
    logging.info("Caching dest plates: %s" % list(dests))
    for dest_barcode, dtype in dests:
        try:
            dest_plate = db_session.query(Plate).\
                filter(Plate.external_barcode == dest_barcode).one()
        except:
            logging.info("There is no dest plate [%s], creating it",
                         dest_barcode)
            # This is a transform that requires creating a new plate in DB
            try:
                dest_plate = create_destination_plate(
                    db_session,
                    operator,
                    dest_barcode,
                    dtype,
                    "Unknown location",  # FIXME find a better default for this
                    transform_template_id)
                db_session.flush()
            except IndexError:
                err_msg = ("Encountered error creating sample "
                           "transform. Could not create destination plate: [%s]"
                           )
                logging.error(err_msg)
                return {
                    "success": False,
                    "errorMessage": err_msg
                }
        plate_cache[dest_barcode] = dest_plate

    # We always operate on a plate's current contents so we pull that
    # in a single query so that we have it on-hand and can then single out
    # samples for transfer.
    sample_cache = {}
    logging.info("Caching plate samples.")
    for barcode, plate in plate_cache.iteritems():
        for sample in plate.current_well_contents(db_session):
            sample_cache[(barcode, sample.well.well_number)] = sample

    # Inject well_number by well_name into transform_map, if necessary.
    # Also make sure source_well_sample is consistent.
    for oper in transform_map:
        if "source_well_number" not in oper:
            s_bc = oper["source_plate_barcode"]
            s_label = str(oper["source_well_name"])
            s_type = plate_cache[s_bc].plate_type
            s_num = s_type.layout.get_well_by_label(s_label).well_number
            oper["source_well_number"] = s_num

        if "source_well_sample" in oper:
            assert oper["source_well_sample"] == sample_cache[(s_bc, s_num)].id

        if "destination_well_number" not in oper:
            # lookup well number based on well name
            # (quick fix for qpix log upload)
            # not sure how many other code paths reach this
            # TODO: if destination_well_number is in oper, crosscheck well_name
            # TODO: speed up / cache dest_type query as needed
            d_type = oper["destination_plate_type"]
            dest_type = db.session.query(PlateType).get(d_type)
            d_label = str(oper["destination_well_name"])
            d_num = dest_type.layout.get_well_by_label(d_label).well_number
            oper["destination_well_number"] = d_num

    # Also cache a map of well number to well instance for all dest wells
    well_cache = {}
    for dest_barcode in [d[0] for d in dests]:
        plate = plate_cache[dest_barcode]
        well_ids = [d['destination_well_number']
                    if d['destination_plate_barcode'] == dest_barcode
                    else None for d in transform_map]
        well_ids = [x for x in well_ids if x is not None]  # There must be a better way!
        for id in well_ids:
            well_cache[(dest_barcode, id)] = plate.get_well_by_number(id)

    dest_grouping_func = lambda oper: (oper['destination_plate_barcode'],
                                       oper['destination_well_number'])
    transforms_by_dest = sorted(transform_map, key=dest_grouping_func)

    new_rows = []
    logging.info("Iterating over operations...")
    for dest_key, operation_group in itertools.groupby(transforms_by_dest,
                                                       dest_grouping_func):

        # get sources -- one or more going into the same destination
        source_samples = [sample_cache[(oper["source_plate_barcode"],
                                        oper["source_well_number"])]
                          for oper in operation_group]

        # get destination plate
        destination_plate_barcode, destination_well_number = dest_key
        try:
            dplate = plate_cache[destination_plate_barcode]
        except KeyError:
            msg = "Failed to lookup destination plate from cache [%s]."
            msg %= destination_plate_barcode
            logging.error("Invalid xfer: %s", msg)
            return {"success": False, "errorMessage": msg}

        # get "destination" sample (if present, this will become a parent)
        try:
            dest_s = sample_cache[(destination_plate_barcode,
                                   destination_well_number)]
        except KeyError:
            dest_s = None

        # create and add the new sample
        s = sample_handler(sample_transform,
                           well_cache, source_samples, dplate,
                           destination_well_number, dest_s)
        new_rows.append(s)

    logging.info("Bulk inserting samples and relationships.")

    db_session.bulk_save_objects(new_rows)
    db_session.commit()

    logger.info("Adhoc movement took: " + str(time.time() - start))

    return {
        "success": True
    }

def sample_handler(transform, well_cache,
                   source_samples, destination_plate,
                   destination_well_id, destination_sample=None):
    """Accessions a new sample into the designated well,
    using the source sample(s) as parents and/or reagents.
    Note: destination_sample, if present, will become a parent
    of the new sample that this routine will accession.

    This loop is now oriented towards many-samples-to-one-sample
    merge transforms.  len(source_samples) shows the type of transform:

        len(source_samples) == 1: copy sample, one parent
        len(source_samples) >  1: merge sample, multiple parents
        len(source_samples) == 0: create fresh sample, no parents (TBD)

    Handles mixing operations (pooling, NGS barcoding, primer addition,
    etc) as well as non-mixing steps
    """

    logging.debug("sample_handler: src=[%s] dest=[%s]", source_samples, destination_sample)

    new_id = create_unique_id(Sample.id_prefix)

    parent_samples = ( source_samples + [destination_sample]
                       if destination_sample
                       else source_samples )

    # Copy all extant metadata except transform, parents, children, is_clonal
    if len(parent_samples) > 1:
        logging.debug("Merging %d samples into %s",  len(parent_samples), destination_sample)
        new_s = merged_sample(parent_samples)
    elif len(parent_samples) == 1:
        new_s = copied_sample(parent_samples[0])
    else:
        raise NotImplementedError("TODO?: Create samples from scratch")

    # Set attributes unique to this sample
    new_s.id = new_id()
    new_s.plate_id = destination_plate.id
    new_s.transform_id = transform.id
    new_s.operator_id = g.user.operator_id
    well = well_cache[(destination_plate.external_barcode, destination_well_id)]
    new_s.plate_well_code = well.well_code

    # Type-specific logic
    handle_transform_specific_logic(transform, new_s)
    true_parent_samples = parse_source_reagents(transform, parent_samples, new_s)

    # Link up the source sample(s) and any "destination" sample as parent(s)
    for source_well_sample in true_parent_samples:
        source_to_dest_well_transform = TransformDetail(
            transform=transform,
            parent_sample_id=source_well_sample.id,
            child_sample_id=new_s.id
        )

    # NOTE that we don't have to explicitly return the TransformDetail instances
    # created because they are linked to the new_s Sample instance by virtue
    # of setting new_s.id as the child ID. I tried to actually return these
    # instances directly and commit them in the bulk commit and that ended up
    # double-adding rows for each TransformDetail, one with the transform ID
    # properly populated and one without. Strange!
    #
    # I also verified that even though nothing is technically *done* with the
    # instance variables source_to_dest_well_transform and
    # dest_well_parent_transform that it *is* necessary to create them like
    # this to properly populated the TransformDetail table. SQLAlchemy magic.

    return new_s

def handle_transform_specific_logic(transform, new_s):

    if transform.transform_type_id in (constants.TRANS_TYPE_QPIX_PICK_COLONIES,
                                       constants.TRANS_TYPE_QPIX_TO_384_WELL):
        # We just cloned this so it's clonal
        new_s.is_clonal = True
        # FIXME hardcoding this for Warp2 for now - should derive
        # this from the A/B rebatching metadata somehow (@sucheta!)
        new_s.cloning_process_id = 'CLO_564c1af300bc150fa632c63d'

def parse_source_reagents(transform, source_samples, new_s):
    """Splits out the "reagent" style parent samples (e.g. ngs barcodes)
    from the true parent samples (e.g. oligo pooling)."""

    true_parent_samples = []

    for src_s in source_samples:
        if src_s.id[0:4] == 'BCS_':
            # FIXME: identify barcode samples via a type_id instead?
            logging.info("REAGENT: %s", src_s)
            n_added = 0
            for attrname in ('i5_sequence_id', 'i7_sequence_id'):
                seq_id = getattr(src_s, attrname)
                if seq_id is not None:
                    setattr(new_s, attrname, seq_id)
                    n_added += 1
            if n_added == 0:
                raise ValueError("%s is missing barcode seqs, please add"
                                 % src_s)
            elif n_added > 1:
                raise ValueError("%s has too %d barcode seqs, expected only 1"
                                 % (src_s, n_added))
        else:
            true_parent_samples.append(src_s)

    return true_parent_samples

def safe_sqlalchemy_copy(session, object_handle):
    """Copy a sample, this approach copies a little too much,
    including parent transform details etc.

    Implement a safe copy.copy().

    Based on http://permalink.gmane.org/gmane.comp.python.sqlalchemy.user/39675

    SQLAlchemy-mapped objects travel with an object
    called an InstanceState, which is pegged to that object
    specifically and tracks everything about that object.  It's
    critical within all attribute operations, including gets
    and deferred loading.   This object definitely cannot be
    shared among two instances, and must be handled.

    The copy routine here makes use of session.merge() which
    already essentially implements a "copy" style of operation,
    which produces a new instance with a new InstanceState and copies
    all the data along mapped attributes without using any SQL.

    The mode we are using here has the caveat that the given object
    must be "clean", e.g. that it has no database-loaded state
    that has been updated and not flushed.   This is a good thing,
    as creating a copy of an object including non-flushed, pending
    database state is probably not a good idea; neither represents
    what the actual row looks like, and only one should be flushed.

    """
    copy = session.merge(object_handle, load=False)
    session.expunge(copy)
    return copy

def transient_copy(session, inst):
    """Copy a sample, this approach forces a bunch of flushes"""
    from sqlalchemy.orm import make_transient
    session.expunge(inst)
    make_transient(inst)
    inst.id = None
    session.add(inst)
    session.flush()
    return inst

def copied_sample(orig_obj):
    """Copy a sample, this approach is hard to maintain though"""

    copy = Sample()
    attrs_to_ignore = ("details", "operator_id")
    attrs_to_propagate = ("order_item_id", "type_id",
                          "external_barcode", "name", "description",
                          "work_order_id", "synthesis_run_pk", "cluster_num",
                          "cloning_process_id",
                          "mol_type", "is_circular", "is_clonal",
                          "is_assembly", "is_external", "external_id",
                          "host_cell_pk", "growth_medium", "i5_sequence_id",
                          "i7_sequence_id")
    for attrname in attrs_to_propagate:
        setattr(copy, attrname, getattr(orig_obj, attrname))
    return copy

def filter_real_samples( samples ):
    from twistdb.work_order import Vector
    # FIXME: this is a hacky sort of way to filter out VECtors, but we can't actually use
    #    the Sample.order_item relationship until the sample itself has been commited to the db
    return [s for s in samples if s.order_item_id and not s.order_item_id.startswith('VEC')]

def merged_sample(parent_samples):
    """Merge a set of samples."""

    filtered_parents = filter_real_samples( parent_samples )

    result = Sample()
    attrs_to_ignore = ("is_clonal", "details", "operator_id")
    attrs_to_merge = ("order_item_id", "type_id",
                      "external_barcode", "name", "description",
                      "work_order_id", "synthesis_run_pk", "cluster_num",
                      "cloning_process_id",
                      "mol_type", "is_circular",
                      "is_assembly", "is_external", "external_id",
                      "host_cell_pk", "growth_medium", "i5_sequence_id",
                      "i7_sequence_id")
    for attrname in attrs_to_merge:
        source_vals = set([getattr(par, attrname) for par in filtered_parents])
        if len(source_vals) == 1:
            setattr(result, attrname, source_vals.pop())
    if len(parent_samples) > 1:
        result.is_pooled = True

    return result


import curses
from curses import panel
from .getLongestItemLength import getLongestItemLength

class OptionMenu(object):

    def loadConfigFile(self):
        with open("config.txt", 'r') as infile:
            lines = infile.readlines()
            for line in lines:
                splitLine = line.split('=')
                varName = splitLine[0]
                value = splitLine[1]
                if varName == 'leftHandVim':
                    if value[:-1] == 'True':
                        self.leftHandVim = True
                    else:
                        self.leftHandVim = False
                if varName == 'rightHandVim':
                    if value[:-1] == 'True':
                        self.rightHandVim = True
                    else:
                        self.rightHandVim = False

    def __init__(self, title, stdscreen):
        maxY, maxX = stdscreen.getmaxyx()
        self.title = title
        self.leftHandVim = True
        self.rightHandVim = True
        self.loadConfigFile()
        leftHandVimStr = f"Left-hand Vim-like {self.leftHandVim} "
        rightHandVimStr = f"Right-hand Vim-like {self.rightHandVim} "
        self.items = [
            [leftHandVimStr, None],
            [rightHandVimStr, None],
            ["Exit", None]
        ]
        self.position = 0
        rowPad = 6
        colPad = 20
        rows = len(self.items) + rowPad
        cols = len(rightHandVimStr) + colPad
        y = maxY // 2 - (rows//2)
        x = maxX // 2 - (cols//2)
        self.window = stdscreen.subwin(rows, cols , y, x)
        self.window.keypad(1)
        self.panel = panel.new_panel(self.window)
        self.panel.hide()
        panel.update_panels()

    def navigate(self, n):
        self.position += n
        if self.position < 0:
            self.position = 0
        elif self.position >= len(self.items):
            self.position = len(self.items) - 1

    def display(self):
        self.panel.top()
        self.panel.show()
        self.window.clear()
        rows, cols = self.window.getmaxyx()
        while True:
            self.window.refresh()
            curses.doupdate()
            self.window.border('|','|','-','-','+','+','+','+')
            offsetY = 1
            offsetX = 1
            self.window.addstr( offsetY, offsetX, self.title )
            offsetX += 2
            offsetY += 2
            for index, item in enumerate(self.items):
                mode = curses.A_NORMAL
                if index == self.position:
                    mode = curses.A_REVERSE
                msg = f"{index}. {item[0]}"
                self.window.addstr(offsetY + index, offsetX, msg, mode)

            key = self.window.getch()
            if key in [curses.KEY_ENTER, ord("\n")]:
                selectedItem = self.items[self.position][0]
                if "Exit" in selectedItem:
                    break
                elif "Left-hand Vim-like" in selectedItem:
                    self.leftHandVim = not self.leftHandVim
                    self.items[self.position][0] = f"Left-hand Vim-like {self.leftHandVim} "
                elif "Right-hand Vim-like" in selectedItem:
                    self.rightHandVim = not self.rightHandVim
                    self.items[self.position][0] = f"Right-hand Vim-like {self.rightHandVim} "
            elif key == curses.KEY_UP:
                self.navigate(-1)
            elif key == curses.KEY_DOWN:
                self.navigate(1)

        self.writeOptionsToFile()

        self.window.clear()
        self.panel.hide()
        panel.update_panels()
        curses.doupdate()

    def writeOptionsToFile(self):
        with open('config.txt','w') as outfile:
            outfile.write(f"leftHandVim={self.leftHandVim}\n")
            outfile.write(f"rightHandVim={self.rightHandVim}\n")


from django.db import models
from django.db.models import Manager
from django.core.cache import cache
from django.contrib.auth.models import User

from articulate_app.validators import validate_image, validate_rating, validate_imdb_rating
from articulate_app.service import create_embed_link

class Article(models.Model):
    """
    Model to save article added by django default user
    """
    TYPE_CHOICE = [
        ("Series", "series"),
        ("Documentary", "documentary"),
        ("Short Film", "short Film"),
        ("Youtube", "youtube"),
        ("Movie", "movie"),
        ("Album", "album"),
    ]
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    modified_at = models.DateTimeField(auto_now=True, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE, help_text='Select yourself from dropdown')
    rating = models.FloatField(verbose_name="Your Rating", validators=[validate_rating], help_text='Decimal 0.1 to 5.0')
    title = models.CharField(verbose_name="Title", max_length=255, help_text='Keep 1st letter Capital')
    type = models.CharField(verbose_name="Type", max_length=50, choices=TYPE_CHOICE, default=TYPE_CHOICE[4][0],
                            help_text='default is Movie')
    creatives = models.CharField(verbose_name="Director/Creation/Youtuber", max_length=100, default="Not Mentioned")
    review = models.TextField(verbose_name="Review Text")
    embed_link = models.URLField(verbose_name="Trailer Youtube Link", blank=True, null=True,
                                 help_text='Paste only youtube urls')
    image = models.FileField(verbose_name="Movie Poster", validators=[validate_image],
                             help_text='Maximum file size allowed is 300kb', unique=True, null=True)
    imdb_rating = models.FloatField(verbose_name="IMDB Rating", validators=[validate_imdb_rating],
                                    help_text='Decimal 0.1 to 10.0')

    objects = Manager()

    def save(self, *args, **kwargs):
        if self.embed_link:
            self.embed_link = create_embed_link(link=self.embed_link)
        cache.clear()
        super(Article, self).save(*args, **kwargs)

    def __str__(self):
        return f"{self.title}"


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# mf2 to jf2 converter
# licence cc0
#  2015 Kevin Marks

import logging

def flattenProperties(items):
    if type(items) is list:
        if len(items) <1:
            return {}
        if len(items)== 1:
            item = items[0]
            if type(item) is dict:
                if item.has_key("type"):
                    props ={"type":'-'.join(item.get("type",["-"])[0].split("-")[1:])}
                    properties =  item.get("properties",{})
                    for prop in properties:
                        props[prop] = flattenProperties(properties[prop])
                    children  =  item.get("children",[])
                    if children:
                        if len(children) == 1:
                            props["children"] =[flattenProperties(children)]
                        else:
                            props["children"] =flattenProperties(children)["children"]
                    return props

                elif item.has_key("value"):
                    return item["value"]
                else:
                    return ''
            else:
                return item
        else:
            return {"children":[flattenProperties([child]) for child in items]}
    else:
        return items #not a list, so string

def mf2tojf2(mf2):
    """I'm going to have to recurse here"""
    jf2={}
    items = mf2.get("items",[])
    jf2=flattenProperties(items)
    #print jf2
    return jf2


# Advent of code 2022, by Ghostkeeper

import get_input_file
import grid_path

lines = open(get_input_file.get_path(12, 1)).readlines()
lines = [line.strip() for line in lines]
grid = [[ord(char) - ord("a") for char in line] for line in lines]

starts = []
end_x = -1
end_y = -1
for x in range(len(grid[0])):
	for y in range(len(grid)):
		if lines[y][x] == "S":
			grid[y][x] = 0
			starts.append((x, y))
		if lines[y][x] == "a":
			starts.append((x, y))
		elif lines[y][x] == "E":
			end_x = x
			end_y = y
			grid[y][x] = 25

shortest_length = 9999999999
for x, y in starts:
	try:
		shortest_path = grid_path.find_path(grid, x, y, end_x, end_y)
		shortest_length = min(shortest_length, len(shortest_path))
	except:
		pass  # Not every start point has a path to the destination.
print("The length of the shortest path from any a is:", shortest_length)