import bpy
import math
from mathutils import Vector, geometry, Euler
from enum import Enum
import sys

# -------------------------------------------------------------------------------------------------------
# CONTOUR SOLVER FUNCTIONS
# Used to infer the 3D shape of a 2D grease pencil drawing

def polarToCartesian(r, phi, theta):  # theta = v, phi = u
    """radius r, inclination theta, azimuth phi in DEGREES"""
    # https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
    # theta 0: N pole
    # theta 180: S pole
    # phi: anticlockwise from above
    theta = math.radians(theta)
    phi = math.radians(phi)
    x = r * math.sin(theta) * math.cos(phi)
    y = r * math.sin(theta) * math.sin(phi)
    z = r * math.cos(theta)
    return ((x, y, z))

def cartesianToPolar(x, y, z):
    """returns radius, phi, theta, in DEGREES where:
     +x axis is (0,90)
     -x axis is (180,90)
     +y axis is (90,90)
     -y axis is (-90,90)
     +z axis is (0,0)
     -z axis is (0,180)
    """
    r = math.sqrt(x * x + y * y + z * z)
    theta = math.acos(z / r)
    phi = math.atan2(y, x)
    return ((r, math.degrees(phi), math.degrees(theta)))  # uv IN DEGREES!!!!

def degreesToFirstPositiveDegrees(angle):
    """ puts an angle in the 0->360 range"""
    answer = math.fmod(angle, 360)
    if answer < 0:
        answer += 360
    return answer

def averageVectors(vectorList):
    """Return the (component-wise) average of the input vector list"""
    answer = Vector()
    nVectors = len(vectorList)
    for v in vectorList:
        answer += v / nVectors
    return answer

def colinear(p0, p1, p2):
    """from https://stackoverflow.com/questions/9608148/
    python-script-to-determine-if-x-y-coordinates-are-colinear-getting-some-e"""
    x1, y1 = p1[0] - p0[0], p1[1] - p0[1]
    x2, y2 = p2[0] - p0[0], p2[1] - p0[1]
    return abs(x1 * y2 - x2 * y1) < 1e-12

class materialNames():
    """Get a dict of the required materials"""
    # TODO: this seems like a sloppy way of doing this, what's the proper way of doing this?
    def __init__(self):
        prefix = 'Pipecleaner_'
        self.x = prefix + 'X'
        self.y = prefix + 'Y'
        self.z = prefix + 'Z'
        self.arbitrary = prefix + 'W'
        self.intersection = prefix + 'intersection'
        self.rough = prefix + 'rough'
        self.allMaterialNames = [self.x, self.y, self.z, self.arbitrary, self.intersection, self.rough]

class StrokeType(Enum):
    marker           = 0
    planar_axial     = 1
    planar_arbitrary = 2
    undefined        = 3

class Camera():
    # simple view class
    def __init__(self, cam):
        print('setting up camera')
        self.camera = cam
        self.origin = cam.location
        # TODO: we're not actually using rotation now we have direction vector, right?
        # rotation_euler = cam.rotation_euler
        # if rotation_euler.order != 'XYZ':
        #     raise(Exception('This has only been tested with the default XYZ rotation order on cameras,
        # you probably wanna change that back'))
        # self.heading = math.degrees(rotation_euler[0])  # in radians, we actually might not even need this?!
        # self.elevation = math.degrees(rotation_euler[2])
        self.direction = cam.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))  # it's a vector now
        # https://blender.stackexchange.com/questions/13738/how-to-calculate-the-direction-and-up-vector-of-a-camera
        print('got camera, position', self.origin, ' direction: ', self.direction)

# -------------------------------------------------------------------------------------------------------
class PlanarStrokeCluster():
    """Contains a load of AXIAL, PLANAR strokes which are (directly or indirectly)
    connnected to each other as well references to all the Arbitrary planar strokes"""

    def __init__(self, firstStroke):
        """initialise from just Axial Planar Strokes.
        We give it one stroke and it should connect the dots"""
        # self.strokes = [firstStroke] # should it be a set? those can't be mutable, though
        self.strokes = [firstStroke]
        # add all connected planar strokes
        self.strokes += firstStroke.allConnectedPlanarStrokes(strokeTypes=[StrokeType.planar_axial],
                                                              connectionList=[firstStroke])
        # we DON'T need to give it existing clusters because all strokes in
        # that cluster would already be in a cluster and by definition can't
        # be connected to this one
        self.potentialConnections = self.replaneCluster(testOnly=True)  # find all the potential connections

    def __repr__(self):
        """The print statement"""
        return ('PlanarStrokeCluster with ' + str(len(self.strokes)) + ' strokes and '
                + str(len(self.potentialConnections)) + ' potential connections')

    def __lt__(self, other):
        """ the one with less connections is less"""
        nSelfConnections = len(self.potentialConnections)
        nOtherConnections = len(other.potentialConnections)
        return nSelfConnections < nOtherConnections

    def mostConnectedStroke(self):
        self.strokes.sort(reverse=True)
        if len(self.strokes) > 0:
            return self.strokes[0]
        else:
            raise(Exception("could not get 'most connected stroke' from empty stroke cluster"))

    def replaneCluster(self, testOnly=False):
        """ ok so this either tries to, or does, propogate from the mostConnectedStroke through the
        whole network, alternately doing rounds of axial and arbitrary strokes"""
        # set up the first stroke or it ain't gonna work!
        firstStroke = self.mostConnectedStroke()

        if testOnly is False:
            successfullyCalculatedNormalsAndOrigin = firstStroke.calculateNormalAndOrigin()
            firstStroke.rePlane()
            if not successfullyCalculatedNormalsAndOrigin:
                raise(Exception("Could not calculate origin and normals of first stroke "))
        currentlyConnectedStrokes = [firstStroke]

        i = 0
        MAX_ITERATIONS = 9999  # hmmmm not sure this is good coding practise?

        # ok we're gonna keep expanding outwards, alternatley by axial strokes (1 connection required) and
        # arbitrary strokes (3 connections required)
        # we're gonna keep doing this until we stop getting new connections...
        while i < MAX_ITERATIONS:
            i += 1  # iterate the counter, just to be safe :-)

            # keep track of what strokes have been added in this iteration -
            # if we don't get any new ones then we got em all
            newlyConnectedStrokes = []
            # alternate between doing axial and arbitrary strokes
            for strokeType in [StrokeType.planar_axial, StrokeType.planar_arbitrary]:
                # this will increase (hopefully) on each iteration
                for stroke in currentlyConnectedStrokes:
                    # get all the strokes directly attached to the current one
                    for potentialNewConnection in stroke.adjacentPlanarStrokes():
                        if potentialNewConnection.strokeType == strokeType:
                            # if they're not already connected (some will have multiple connections,
                            # and we don't wanna repeat)
                            if potentialNewConnection not in currentlyConnectedStrokes + newlyConnectedStrokes:
                                # seems to be a new one!

                                # ok, so the simpler version is to just 'try to replane' here, if it works it works
                                cs = currentlyConnectedStrokes + newlyConnectedStrokes
                                success = potentialNewConnection.calculateNormalAndOrigin(connectedStrokes=cs,
                                                                                testOnly=testOnly)

                                if success:
                                    if testOnly is False:
                                        potentialNewConnection.rePlane()
                                    newlyConnectedStrokes.append(potentialNewConnection)    # add them to the list

            if len(newlyConnectedStrokes) > 0:
                # if we got any new ones
                currentlyConnectedStrokes += newlyConnectedStrokes  # plop them on there
            else:
                break  # nothing to see here, think we got em all!

        return currentlyConnectedStrokes

# -------------------------------------------------------------------------------------------------------
class Stroke():
    # containts reference to the stroke, methods for getting its screen space data, etc
    # extended for the 2 types of strokes: planar strokes and intersection markers

    def __init__(self, gpStroke, camera):
        # should intitialize it by passing it the grease pencil stroke I guess?
        # whatever internal stuffs it has
        self.gpStroke = gpStroke
        self.polarPoints = []
        self.intersections = {}  # this is going to be key/value pairs, with key = object and value = polar coordinate
        self.bBox_phiMin = None
        self.bBox_phiMax = None
        self.bBox_thetaMin = None
        self.bBox_thetaMax = None
        self.cameraOrigin = camera.origin
        self.origin = None  # where it is in 3d space (point for a marker, origin for a planar stroke)
        self.normal = None
        self.hasBeenPlaced = False
        self.strokeType = StrokeType.undefined  # override this!

        # calculate polar points
        for point in self.gpStroke.points.values():
            co = point.co
            coFromOrigin = co - self.cameraOrigin
            polarCoordinate = cartesianToPolar(coFromOrigin[0], coFromOrigin[1], coFromOrigin[2])

            # keep track of screen-space bounding box
            if self.bBox_phiMin   is None or polarCoordinate[1] < self.bBox_phiMin:
                self.bBox_phiMin   = polarCoordinate[1]
            if self.bBox_phiMax   is None or polarCoordinate[1] > self.bBox_phiMax:
                self.bBox_phiMax   = polarCoordinate[1]
            if self.bBox_thetaMin is None or polarCoordinate[2] < self.bBox_thetaMin:
                self.bBox_thetaMin = polarCoordinate[2]
            if self.bBox_thetaMax is None or polarCoordinate[2] > self.bBox_thetaMax:
                self.bBox_thetaMax = polarCoordinate[2]

            self.polarPoints.append(polarCoordinate)

        # ok, now we've got all the polar points, we should have a reasonable bounding box?

        # make sure the bbox wraps correctly at 360 degrees
        self.bBox_phiMin   = degreesToFirstPositiveDegrees(self.bBox_phiMin)
        self.bBox_phiMax   = degreesToFirstPositiveDegrees(self.bBox_phiMax)
        self.bBox_thetaMin = degreesToFirstPositiveDegrees(self.bBox_thetaMin)
        self.bBox_thetaMax = degreesToFirstPositiveDegrees(self.bBox_thetaMax)
        if self.bBox_phiMax   < self.bBox_phiMin:
            self.bBox_phiMax   += 360
        if self.bBox_thetaMax < self.bBox_thetaMin:
            self.bBox_thetaMax += 360

    def __repr__(self):
        """the print statement"""
        return ('Stroke: ' + str(len(self.polarPoints))
                + ' points, type: ' + str(self.strokeType.name)
                + ', polarBBox = ' + str(self.bBox_phiMin) + ', ' + str(self.bBox_phiMax)
                + ', ' + str(self.bBox_thetaMin) + ', ' + str(self.bBox_thetaMax) + ', '
                + str(len(self.intersections)) + ' intersections')

    def bBoxArea(self):
        return (self.bBox_phiMax - self.bBox_phiMin) * (self.bBox_thetaMax - self.bBox_thetaMin)

    def addIntersection(self, intersectingObject, polarCoordinate):
        """ used for storing intersectionMarkers on planarStrokes and vice versa"""
        # print ('adding intersection', intersectingObject, polarCoordinate)
        self.intersections[intersectingObject] = polarCoordinate  # that should do it, right?
        # doesn't check if they already intersecting, which might be ok because sometimes
        # we'll want several markers on the same stroke

    # -------------------------------------------------------------------------------------------------------
    # from https://stackoverflow.com/questions/3838329/how-can-i-check-if-two-segments-intersect
    def on_segment(self, p, q, r):
        '''Given three colinear points p, q, r, the function checks if
        point q lies on line segment "pr"
        '''
        if (q[0] <= max(p[0], r[0])
           and q[0] >= min(p[0], r[0])
           and q[1] <= max(p[1], r[1])
           and q[1] >= min(p[1], r[1])):
            return True
        return False

    def orientation(self, p, q, r):
        '''Find orientation of ordered triplet (p, q, r).
        The function returns following values
        0 --> p, q and r are colinear
        1 --> Clockwise
        2 --> Counterclockwise
        '''

        val = ((q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]))
        if val == 0:
            return 0  # colinear
        elif val > 0:
            return 1   # clockwise
        else:
            return 2  # counter-clockwise

    def do_intersect(self, p1, q1, p2, q2):
        '''Main function to check whether the closed line segments p1 - q1 and p2
           - q2 intersect'''
        o1 = self.orientation(p1, q1, p2)
        o2 = self.orientation(p1, q1, q2)
        o3 = self.orientation(p2, q2, p1)
        o4 = self.orientation(p2, q2, q1)

        # General case
        if (o1 != o2 and o3 != o4):
            return True

        # Special Cases
        # p1, q1 and p2 are colinear and p2 lies on segment p1q1
        if (o1 == 0 and self.on_segment(p1, p2, q1)):
            return True

        # p1, q1 and p2 are colinear and q2 lies on segment p1q1
        if (o2 == 0 and self.on_segment(p1, q2, q1)):
            return True

        # p2, q2 and p1 are colinear and p1 lies on segment p2q2
        if (o3 == 0 and self.on_segment(p2, p1, q2)):
            return True

        # p2, q2 and q1 are colinear and q1 lies on segment p2q2
        if (o4 == 0 and self.on_segment(p2, q1, q2)):
            return True

        return False  # Doesn't fall in any of the above cases

    def get_lineIntersectionPoint(self, A, B, C, D):  # expects xy for A, B, C, D
        # from https://stackoverflow.com/questions/3252194/numpy-and-line-intersections
        # a1x + b1y = c1
        a1 = B[1] - A[1]
        b1 = A[0] - B[0]
        c1 = a1 * (A[0]) + b1 * (A[1])

        # a2x + b2y = c2
        a2 = D[1] - C[1]
        b2 = C[0] - D[0]
        c2 = a2 * (C[0]) + b2 * (C[1])

        # determinant
        det = a1 * b2 - a2 * b1

        # parallel line (hmmm they could be colinear, right?
        if det == 0:
            # we already checked that they DO intersect, so just gonna return a point on the line
            # this shouldn't really ever happen!
            return A
            # return (float('inf'), float('inf'))

        # intersect point(x,y)
        x = ((b2 * c1) - (b1 * c2)) / det
        y = ((a1 * c2) - (a2 * c1)) / det
        return ([x, y])

    # -------------------------------------------------------------------------------------------------------
    def intersection(self, b):
        """returns true if this stroke and stroke b intersect in polar space"""
        if self.bBox_phiMax >= b.bBox_phiMin and b.bBox_phiMax >= self.bBox_phiMin:
            if self.bBox_thetaMax >= b.bBox_thetaMin and b.bBox_thetaMax >= self.bBox_thetaMin:
                # ok, the bboxes intersect, let's check the points
                for iSegment in range(len(self.polarPoints) - 1):  # there is 1 less segment than the number of points
                    for jSegment in range(len(b.polarPoints) - 1):
                        lineA_x1 = self.polarPoints[iSegment][1]  # 0 is radius, remember
                        lineA_y1 = self.polarPoints[iSegment][2]
                        lineA_x2 = self.polarPoints[iSegment + 1][1]  # 0 is radius, remember
                        lineA_y2 = self.polarPoints[iSegment + 1][2]
                        lineB_x1 = b.polarPoints[jSegment][1]  # 0 is radius, remember
                        lineB_y1 = b.polarPoints[jSegment][2]
                        lineB_x2 = b.polarPoints[jSegment + 1][1]  # 0 is radius, remember
                        lineB_y2 = b.polarPoints[jSegment + 1][2]

                        if self.do_intersect((lineA_x1, lineA_y1),
                                             (lineA_x2, lineA_y2),
                                             (lineB_x1, lineB_y1),
                                             (lineB_x2, lineB_y2)):
                            # ok and let's find the real intersection:
                            # slopeA = (lineA_x2-lineA_x1)/(lineA_y2-lineA_y1) # doesn't work if y is 0
                            ip = self.get_lineIntersectionPoint([lineA_x1, lineA_y1],
                                                                [lineA_x2, lineA_y2],
                                                                [lineB_x1, lineB_y1],
                                                                [lineB_x2, lineB_y2])
                            return ip  # just the [phi, theta]
        return None

    def rePlane(self):
        """separated this out, it now assumes you've already specified the ORIGIN and the NORMAL"""
        # so, once we know the normal and origin, for every point:
        for point in self.gpStroke.points.values():
            # we know the line it's on (from the camera origin, through the world point (assuming it's different)
            # we know the plane it's on (from the plane origin and normal
            # so we gotta find the intersection
            p0 = self.cameraOrigin  # p0, p1: define the line
            p1 = point.co
            p_co = self.origin  # p_co is a point on the plane (plane coordinate).
            p_no = self.normal  # p_no is a normal vector defining the plane direction (does not need to be normalized).
            # intersectionPoint = isect_line_plane_v3(p0, p1, p_co, p_no) # should not be none!
            intersectionPoint = geometry.intersect_line_plane(p0, p1, p_co, p_no)
            if intersectionPoint is None:
                raise(Exception("A bad thing that shouldn't have happened happened...."))
            # then move it to the new location
            point.co = intersectionPoint

# -------------------------------------------------------------------------------------------------------
class IntersectionMarker(Stroke):
    """subclass of stroke used for intersection markers"""
    def __init__(self, gpStroke, camera):
        super().__init__(gpStroke, camera)
        self.strokeType = StrokeType.marker
        self.normal = camera.direction  # simple enough huh

    def calculateOriginAndRePlane(self):
        """ok we just look at the first placed connected stroke and intersect with that"""
        for intersection in self.intersections.keys():
            if intersection.hasBeenPlaced:
                # get where we intersect
                polarCoordinate = self.intersections[intersection]
                self.origin = intersection.polarToCartesianPositionOfIntesection(polarCoordinate)
                if self.origin:
                    self.rePlane()
                return

# -------------------------------------------------------------------------------------------------------
class PlanarStroke(Stroke):
    """subclass of stroke used for planar strokes, XYZ ('Axial') to start with, adding 'Arbitrary' ones as well"""
    def __init__(self, gpStroke, camera):
        super().__init__(gpStroke, camera)

        # planar stroke attributes
        # self.planeOrigin = None
        # self.hasBeenDefined = False # has it been defined in xyz space or just polar
        # self.cluster = None # used when building clusters I guess?

    def __repr__(self):
        """the print statement"""
        return super().__repr__() + ', normal:' + str(self.normal)

    def __lt__(self, other):
        """used for sorting list"""
        if len(self.intersections) == len(other.intersections):
            return len(self.polarPoints) < len(other.polarPoints)
        else:
            return len(self.intersections) < len(other.intersections)

    def getScreenSpaceIntersections(self, other):
        """return list of screenspace coordinate of intersections if they intersect, otherwise []
        Needs to handle multiple intersections if they exist! """
        screenSpaceIntersections = []

        # TODO: check if multiple marker intersections even work!
        if len(self.intersections) == 0:
            return screenSpaceIntersections
        if len(other.intersections) == 0:
            return screenSpaceIntersections

        for marker in self.intersections.keys():
            for intersectingStroke in marker.intersections.keys():
                if intersectingStroke == other:
                    # ok, so the two strokes intersect at this marker, let's add the screenspace coordinate to the list
                    ssi = self.intersections[marker]  # this should get the intersection position from the dict
                    screenSpaceIntersections.append(ssi)
        return screenSpaceIntersections

    def polarToCartesianPositionOfIntesection(self, polarCoordinate):
        """assuming the plane has been defined (we know its origin and normal), this calculates the cartesian
        position of the polar coordinate (from the camera)"""
        p0 = self.cameraOrigin  # p0, p1: define the line
        p1 = Vector(polarToCartesian(1, polarCoordinate[0], polarCoordinate[1])) + self.cameraOrigin
        # eh gotta get the xyz from the polar, dang. MAYBE CHECK - need to compensate for cam origin
        # not being at zero so added it...
        p_co = self.origin  # p_co is a point on the plane (plane coordinate).
        p_no = self.normal  # p_no is a normal vector defining the plane direction (does not need to be normalized).
        cartesianCoordinate = geometry.intersect_line_plane(p0, p1, p_co, p_no)
        return cartesianCoordinate

    def calculateNormalAndOrigin(self, connectedStrokes=[], testOnly=False):
        """Recalculates all the point coordinates. Uses the strokes in clusterConnections to get the values we need:
        polar coordinates, cameraOrigin, normal and origin
        If it's an arbitrary plane, no normal has been specified, searches all intersecting planes to try to find 3
        that have been placed!
        returns true if it worked, false if it didn't
        """

        # do all the replaning (setting origins, etc) here instead of scattering around

        # sanity checks
        if self.hasBeenPlaced:
            return False  # it's already been done, let's not do it again!
        if self.strokeType not in [StrokeType.planar_arbitrary, StrokeType.planar_axial]:
            raise(Exception("Stroke has unexpected stroke type: " + str(self.strokeType)))

        # special case if this is the first stroke - we just make up the origin
        if connectedStrokes == []:
            if self.strokeType == StrokeType.planar_axial:
                self.origin = Vector()  # if it's the first stroke, and it's axial
            else:
                raise(Exception("initial stroke in rePlane must be of type planar_axial!"))
        else:
            # for all strokes except the first one:

            # figure out how many anchors we're gonna need
            # an anchor is just a *point in cartesian space that we know is on the new plane*
            if self.strokeType == StrokeType.planar_arbitrary:
                minimumRequiredAnchorPoints = 3
            else:
                minimumRequiredAnchorPoints = 1

            # try to find the correct number of anchor points
            anchorPoints = []
            for intersectingStroke in connectedStrokes:
                # we can safely (?) assume that the plane of this one has been defined,
                # otherwise it shouldn't be connected
                intersectionsWithThisStroke = self.getScreenSpaceIntersections(intersectingStroke)
                for intersectionWithThisStroke in intersectionsWithThisStroke:
                    if testOnly:
                        # if we're just testing, we don't need to do the whole shebang
                        anchorPoints.append(Vector())
                        if len(anchorPoints) >= minimumRequiredAnchorPoints:
                            break  # should really break twice, but hey
                    else:
                        # ok, this is the screenspace coordinate, we can get where it is reasonably easily
                        # where the angular line intersects with the replaneStroke's plane
                        il = [intersectionWithThisStroke[0], intersectionWithThisStroke[1]]
                        cartesianCoordinate = intersectingStroke.polarToCartesianPositionOfIntesection(il)
                        if cartesianCoordinate is None:
                            raise(Exception("Intersection point = None on stroke: ", self))
                        anchorPoints.append(cartesianCoordinate)
                        # might as well keep going, rather than stopping, and get all the markers we can?

            # ok, let's see if we have enough!
            if len(anchorPoints) < minimumRequiredAnchorPoints:
                # we don't have enough information to make this work
                return False
            else:
                if testOnly:
                    return True  # if we're just testing, that's all the info we needed!

            # ok, now we know the worldspace position of the intersections
            # calculate the normal (can skip this for axial strokes)
            if self.strokeType == StrokeType.planar_arbitrary:

                print('replaning arbitrary stroke')
                # ok, so we don't know where this should go!!!! It's an ARBITRARY PLANE

                print('arbitrary plane found with ' + str(anchorPoints) + ' anchor points defined')

                # right, we have at least 3 anchor points, we gotta put a plane through them.
                # we already calculated the origin
                # TODO: do all average normals!
                # then for *every possible group of 3* we're gonna calculate the normal
                # then we're gonna flip the normals that are inverted
                # then we're gonna take the average normal

                # placeholder simple mode, just from the first three:
                testPoints = anchorPoints[:3]  # should work, right!
                v1 = testPoints[1] - testPoints[0]
                v2 = testPoints[2] - testPoints[0]
                # TODO: check if they're different yo!
                v1.normalize()
                v2.normalize()

                cross = v1.cross(v2)

                # check they're not colinear
                if cross.length <= 0.002:
                    # sure, could use better epsilon,but the result's gonna be rubbish even
                    # if they're only *almost* colinear
                    return False  # TODO: temp HACK, should really look for BEST points

                self.normal = cross

            # set the origin (this is the same for both arbitrary and axial planar strokes)
            self.origin = averageVectors(anchorPoints)  # I thiiink that's right?

        # self.rePlane()

        # we can also place all the markers now (not necessary but makes things a bit more legible!)

        self.hasBeenPlaced = True  # whoop
        return True  # done!

    def adjacentPlanarStrokes(self):
        """return the planar strokes intersecting with markers intersecting with this that aren't this"""
        answer = []
        for marker in self.intersections:  # all strokes intersecting with a planar stroke are MARKERS
            # go through all strokes connected to the markers
            for intersectingStroke in marker.intersections:
                if intersectingStroke != self:
                    answer.append(intersectingStroke)
        return answer

    def allConnectedPlanarStrokes(self, strokeTypes=[StrokeType.planar_axial], connectionList=[]):
        """recursively returns connected strokes with the specified types. The ConnectionList
        parameter contains previously detected connections, otherwise we're gonna
        end up redoing them loads of times! """
        # go through all strokes to this one
        newConnectionList = []
        for intersectingStroke in self.adjacentPlanarStrokes():
            if intersectingStroke not in connectionList:  # ok, if it hasn't already been done
                if intersectingStroke.strokeType in strokeTypes:
                    newConnectionList.append(intersectingStroke)
                    ncl = intersectingStroke.allConnectedPlanarStrokes(strokeTypes=strokeTypes,
                                                                       connectionList=(connectionList
                                                                                + newConnectionList))  # recurse!
                    newConnectionList += ncl
                pass
            pass
        return newConnectionList

    def highlight(self, select=True):
        if len(self.adjacentPlanarStrokes()) == 0:  # we got nothin'
            self.gpStroke.select = select

def getActiveGreasePencilObject():
    gp = bpy.context.active_object
    if gp is None:
        return None
    if gp.type != 'GPENCIL':
        return None  # raise(Exception("NO ACTIVE GPENCIL OBJECT"))
    return gp

def getActiveGreasePencilStrokes():
    """Gets all the strokes for all the layers for the current frame (optional)
    of the active grease pencil"""
    # TODO: add options for ignoring locked/hidden layers and doing all frames/current frame
    # TODO: wait does it even make sense to do all frames? we would need to do each frame individually,
    # but how would strokes that last multiple frames work? ok, let's just do the current frame :-)
    strokes = []
    gp = getActiveGreasePencilObject()
    if gp is None:
        return strokes  # which will have length = 0

    respectLocked = bpy.context.scene.pipecleaner_properties.solve_respectLockedLayers
    respectHidden = bpy.context.scene.pipecleaner_properties.solve_respectHiddenLayers

    # layer = gp.data.layers.active # nah let's do all layers
    for layer in gp.data.layers:
        if respectLocked and layer.lock:
            continue  # skip locked layers if we're told to
        if respectHidden and layer.hide:
            continue  # skip hidden layers if we're told to
        frames = layer.frames.values()
        currentFrameFound = False
        for frame in frames:
            if frame.frame_number == bpy.context.scene.frame_current:  # only current frame
                currentFrameFound = True
                strokes += frame.strokes.values()  # add them on to the strokes
        if currentFrameFound is False:
            # raise(Exception("couldn't find any gpencil data for current frame, doing nothing!"))
            continue  # ah, nothing on this layer, keep goin
    return strokes

def getStrokeData(camera):
    """goes through all the grease pencil layers for the active frame and gp,
    and sorts into strokes and intersectionMarkers"""
    planarStrokes = []
    intersectionMarkers = []

    gp = getActiveGreasePencilObject()
    if gp is None:
        raise(Exception('No active grease pencil object'))

    strokes = getActiveGreasePencilStrokes()

    if len(strokes) == 0:
        raise(Exception("No strokes found!"))

    for stroke in strokes:
        # nPoints =  len(stroke.points.values())
        # print ('checking curve with ', nPoints, ' points')
        # check the material
        itsAMarker = False
        # check material
        materialIndex = stroke.material_index
        # or nPoints < INTERSECTION_MARKER_THRESHOLD: # TODO: set up separate marker detection based on bbox
        if materialIndex == gp.data.materials.keys().index(materialNames().intersection):
            # it's an intersection marker
            # set the material (maybe redundant)
            itsAMarker = True
        # else:
        #     # make a temp stroke to check area
        #     tempStroke = Stroke(stroke, camera)
        #     bBoxArea = tempStroke.bBoxArea()
        #     if bBoxArea < INTERSECTION_MARKER_THRESHOLD:
        #         itsAMarker = True
        #         # set the mat, cos it wasn't set
        #         stroke.material_index = gp.data.materials.keys().index(materialNames().intersection)
        #         # by material name

        if itsAMarker:
            # create an intersectionLine from it
            newIntersectionMarker = IntersectionMarker(stroke, camera)
            intersectionMarkers.append(newIntersectionMarker)
        else:
            # it's some kind of planar stroke
            # get the material index

            normal = None
            strokeType = StrokeType.planar_axial
            if materialIndex == gp.data.materials.keys().index(materialNames().x):
                normal = Vector((1, 0, 0))
            elif materialIndex == gp.data.materials.keys().index(materialNames().y):
                normal = Vector((0, 1, 0))
            elif materialIndex == gp.data.materials.keys().index(materialNames().z):
                normal = Vector((0, 0, 1))
            elif materialIndex == gp.data.materials.keys().index(materialNames().arbitrary):
                # arbitrary plane!
                normal = None
                strokeType = StrokeType.planar_arbitrary
            else:
                print("Stroke has unknown material (", materialIndex, "), ignoring!")
                continue

            newPlanarStroke = PlanarStroke(stroke, camera)
            newPlanarStroke.normal = normal
            newPlanarStroke.strokeType = strokeType
            planarStrokes.append(newPlanarStroke)
            # check it against the custom materials

    return (planarStrokes, intersectionMarkers)

def getClusters(planarStrokes):
    """This should hopefully get all the clusters of contiguous axial planar strokes, and return them"""
    # create clusters of all directly-connected axial strokes by :
    # pick a stroke

    print('getting clusters....')

    clusters = []
    for planarStroke in planarStrokes:  # then for every planar stroke
        if planarStroke.strokeType == StrokeType.planar_axial:  # only do this for axial ones
            #   is the stroke in any existing cluster?
            strokeInCluster = False
            for cluster in clusters:
                if planarStroke in cluster.strokes:
                    strokeInCluster = True
                    break
            if strokeInCluster is False:  # if not
                newCluster = PlanarStrokeCluster(planarStroke)  # create a cluster, populate it
                clusters.append(newCluster)

    print('done, found', len(clusters), ' clusters')
    return clusters

def getCameraObject():
    """searches all the objects until it finds the user of the camera datablock"""
    for o in bpy.data.objects:
        # compare the name strings
        if o.data.name_full == bpy.context.scene.pipecleaner_properties.camera:
            return o
    return None

def solveContours():
    """ok, so I guess this is gonna be the big guy"""
    print('-' * 100 + '\nsolving contours....')

    # get the camera info
    camera = Camera(getCameraObject())  # Camera(bpy.context.scene.camera)

    # build a list of what intersections affect what strokes:
    planarStrokes, intersectionMarkers = getStrokeData(camera)    # build a dict of intersections and a dict of strokes
    print('found ', len(planarStrokes), ' planar strokes, ', len(intersectionMarkers), ' intersection markers')

    # check screen-space intersections and fill in intersection data
    for i in range(len(intersectionMarkers)):
        # print ('checking marker', i)
        for j in range(len(planarStrokes)):
            intersection = intersectionMarkers[i].intersection(planarStrokes[j])
            if intersection is not None:
                # store a REFERENCE to each in the other!
                intersectionMarkers[i].addIntersection(planarStrokes[j], intersection)
                planarStrokes[j].addIntersection(intersectionMarkers[i], intersection)
                # print ('found intersection with stroke ', j)

    # check that we found anything at all
    if (len(planarStrokes) < 2):
        return  # don't need an exception, just quite while we're ahead
        # raise(Exception('Not enough planar strokes found: ' + str(len(planarStrokes))))

    # ok, so, refactored version would be:
    clusters = getClusters(planarStrokes)  # create clusters of all directly-connected axial strokes
    print('calculated clusters')

    clusters.sort(reverse=True)  # sort them by connectedness

    print("Replaning strokes")
    # for each cluster in order
    for cluster in clusters:
        print(cluster)
        #   if it isn't yet connected:
        if cluster.strokes[0].hasBeenPlaced is False:
            # there should always be at least one stroke in a cluster, since it's required to init one!
            # and if one has been placed, they've all been placed
            cluster.replaneCluster()
        else:
            print('already replaned, skipping')
        # note: for arbitrary planar strokes, all parents must be from one cluster

    # not required for the maths, but just to tie things together visually lets position the markers
    for marker in intersectionMarkers:
        marker.calculateOriginAndRePlane()

    # select all strokes not joined to the first cluster
    for i in range(len(clusters)):
        select = (i != 0)
        for stroke in clusters[i].strokes:
            stroke.highlight(select=select)

    return


PARAMS = {
    "--window" : 10,
    "--min_mapq" : 20,
    "--min_af" : 0.1,
    "--tsd_max" : 25,
    "--gap_max" : 5
}

#   --window WINDOW       merge window for identifying TE clusters (default = 10)
#   --min_mapq MIN_MAPQ   minimum mapping quality of alignment (default = 20)
#   --min_af MIN_AF       minimum allele frequency (default = 0.1)
#   --tsd_max TSD_MAX     maximum TSD size (default = 25)
#   --gap_max GAP_MAX     maximum gap size (default = 5)


import numpy as np

# Given a list of elements and an element, the function returns all of the positions at which the element appears in the list.
def get_index_positions(list_of_elems, element):
    index_pos_list = []
    index_pos = 0
    while True:
        try:
            index_pos = list_of_elems.index(element, index_pos)
            index_pos_list.append(index_pos)
            index_pos += 1
        except ValueError as e:
            break
    return index_pos_list

# let i be the rows
# let j be the columns

# returns max score and pointer
def calc_score(grid, i, j):
	match_mismatch = (match if seq1[i - 1] == seq0[j - 1] else mismatch)
	past_score_values = np.array([grid[i - 1][j - 1], grid[i - 1][j], grid[i][j - 1]])
	past_score_values = [e + match_mismatch for e in past_score_values] + [0]
	past_score_pointers = [[i - 1, j - 1], [i - 1, j], [i, j - 1], [0, 0]]

	max_score_value = max(past_score_values)
	max_score_indices = get_index_positions(past_score_values, max_score_value)
	max_score_points = [past_score_pointers[i] for i in max_score_indices]
	return max_score_value, max_score_points

def traceback(grid, pointer):

	align_seq0 = ''
	align_seq1 = ''

	x_rel = 1
	y_rel = 1

	while 0 not in pointer:

		x = pointer[0]
		y = pointer[1]

		next_pointers = grid_pointers[x - 1][y - 1]

		if len(next_pointers) == 1:
			pointer = next_pointers[0]
			x_rel = x - pointer[0]
			y_rel = y - pointer[1]
			align_seq0 = (seq0[y - 1] if x_rel == 1 else '-') + align_seq0
			align_seq1 = (seq1[x - 1] if y_rel == 1 else '-') + align_seq1
			continue

		pointer_scores = []
		for next_pointer in next_pointers:
			x_next = next_pointer[0]
			y_next = next_pointer[1]
			pointer_scores.append(grid[x_next][y_next])

		next_pointer_indices = get_index_positions(pointer_scores, max(pointer_scores))
		next_pointers = [next_pointers[i] for i in next_pointer_indices]

		pointer = next_pointers[0]
		x_rel = x - pointer[0]
		y_rel = y - pointer[1]
		align_seq0 = (seq0[y - 1] if x_rel == 1 else '-') + align_seq0
		align_seq1 = (seq1[x - 1] if y_rel == 1 else '-') + align_seq1

	return align_seq0, align_seq1

if __name__ == '__main__':

	match = 1
	mismatch = -1

	seq0 = 'TACGGGCCCGCTAC'
	seq1 = 'TAGCCCTATCGGTCA'

	grid = np.array([[0 for i in range(len(seq0) + 1)] for j in range(len(seq1) + 1)])
	grid_pointers = [[ [0, 0] for i in range(len(seq0))] for j in range(len(seq1))]

	for i in range(1, len(grid)):
		for j in range(1, len(grid[0])):
			grid[i][j], grid_pointers[i - 1][j - 1] = calc_score(grid, i, j)

	max_score_value = np.max(grid)
	highest_score_pointers = np.argwhere(grid == max_score_value).tolist()

	for i, pointer in enumerate(highest_score_pointers):
		align_seq0, align_seq1 = traceback(grid, pointer)
		print(f'Score: \t\t{max_score_value}')
		print(f'Sequence 0: \t{align_seq0}')
		print(f'Sequence 1: \t{align_seq1}')
		print()

	i = 0

#	   	 0	 1   2   3   4	 5	 6	 7 	 8	 9	 10	 11	 12  13	 14
#			 T   A   C   G   G   G   C   C   C   G   C   T   A   C
'''
array([[ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0],	  0
       [ 0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0],	T 1
       [ 0,  0,  2,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  1],	A 2
       [ 0,  0,  1,  1,  2,  3,  4,  3,  2,  1,  2,  1,  0,  1,  1],	G 3
       [ 0,  0,  0,  2,  1,  2,  3,  5,  6,  7,  6,  7,  6,  5,  6],	C 4
       [ 0,  0,  0,  3,  2,  1,  2,  6,  7,  8,  7,  8,  7,  6,  7],	C 5
       [ 0,  0,  0,  4,  3,  2,  1,  7,  8,  9,  8,  9,  8,  7,  8],	C 6
       [ 0,  1,  0,  3,  3,  2,  1,  6,  7,  8,  8,  8, 10,  9,  8],	T 7
       [ 0,  0,  2,  2,  2,  2,  1,  5,  6,  7,  7,  7,  9, 11, 10],	A 8
       [ 0,  1,  1,  1,  1,  1,  1,  4,  5,  6,  6,  6, 10, 10, 10],	T 9
       [ 0,  0,  0,  2,  1,  0,  0,  5,  6,  7,  6,  7,  9,  9, 11],	C 10
       [ 0,  0,  0,  1,  3,  4,  5,  4,  5,  6,  8,  7,  8,  8, 10],	G 11
       [ 0,  0,  0,  0,  4,  5,  6,  5,  4,  5,  9,  8,  7,  7,  9],	G 12
       [ 0,  1,  0,  0,  3,  4,  5,  5,  4,  4,  8,  8,  9,  8,  8],	T 13
       [ 0,  0,  0,  1,  2,  3,  4,  6,  7,  8,  7,  9,  8,  8,  9],	C 14
       [ 0,  0,  1,  0,  1,  2,  3,  5,  6,  7,  7,  8,  8,  9,  8]]) 	A 15
'''


import json

import requests

from demo.settings import API_KEY, yunpian_text
# from demo.demo import local_settings
#
# API_KEY = local_settings.API_KEY
# yunpian_text = local_settings.yunpian_text

class YunPian(object):

    def __init__(self):
        self.api_key = API_KEY
        self.single_url = 'https://sms.yunpian.com/v2/sms/single_send.json'

    def send_sms(self, code, mobile):
        """
        发单条短信
        云片文档：https://www.yunpian.com/doc/zh_CN/domestic/single_send.html
        :return: True 发送成功， False 发送失败
        """
        parmas = {
            'apikey': self.api_key,
            'mobile': mobile,
            'text': yunpian_text(code),
        }
        response = requests.post(self.single_url, data=parmas)
        res_dict = json.loads(response.text)
        if res_dict.get(code) != 0:
            # 发送成功
            return True, res_dict.get('msg')
        return False, res_dict.get('msg')

if __name__ == '__main__':
    y = YunPian()
    y.send_sms(2020, 13246855446)


import datetime

from sqlalchemy import Column, String, Integer, Date, Sequence
from sqlalchemy.ext.declarative import declarative_base

from gyma.vdv.Entities.EntityBase import EntityBase

from gyma.vdv.db import DBConnection

Base = declarative_base()

class EntityTariff(EntityBase, Base):
    __tablename__ = 'vdv_tariff'

    vdvid = Column(Integer, Sequence('vdv_tariffs_seq'), primary_key=True)
    months = Column(Integer)
    price = Column(Integer)
    sale = Column(Integer)

    json_serialize_items_list = ['vdvid', 'months', 'price', 'sale']

    def __init__(self, months, price, sale):
        super().__init__()
        self.months = months
        self.price = price
        self.sale = sale

    @classmethod
    def add_from_json(cls, data):
        if 'months' in data and 'price' in data and 'sale' in data:
            months = data['months']
            price = data['price']
            sale = data['sale']

            new_entity = EntityTariff(months, price, sale)
            vdvid = new_entity.add()

            try:
                with DBConnection() as session:
                    session.db.commit()
            except Exception as e:
                EntityTariff.delete(vdvid)
                raise Exception('Internal error')
        else:
            raise Exception('Validation exception')
        return vdvid


from unittest import expectedFailure

from PySide import QtCore, QtGui

from schematics.mouse_modes.line_submode.inserting import (
    GetHightowerObjectAtPoint, LineRouteBetweenPoints, EndpointTrees,
    RouteNotFoundException)
from schematics import GridScene
from algorithms import hightower
from logicitems import LineTree
from tests.helpers import wait_until_registry_enumerated
from tests import helpers

class TestHightowerObject(helpers.CriticalTestCase):
    def setUp(self):
        super().setUp()

        self.app = QtGui.QApplication.instance()
        if not self.app:
            self.app = QtGui.QApplication([])

        self.scene = GridScene()

        # wait until all types have been enumerated
        wait_until_registry_enumerated(self.scene, self.app)

    def tearDown(self):
        # FIXME: No idea why this workaround is necessary :(
        self.scene.deleteLater()
        self.scene._core.quit()
        self.scene._core_thread.join()
        self.scene._registry._registry_handler.quit(True)
        self.scene = None

        self.app.processEvents()

        super().tearDown()

    def test_empty_scene(self):
        trees = EndpointTrees(None, None)
        ho = GetHightowerObjectAtPoint(self.scene, (0, 0), (10, 10), trees)

        self.assertIs(ho((0, 0)), None)
        self.assertIs(ho((10, 0)), None)
        self.assertIs(ho((0, 10)), None)
        self.assertIs(ho((10, 10)), None)

    def test_colision_add_item(self):
        and_item = self.scene.registry().instantiate_frontend_item(
            "7793F2A0-B313-4489-ABF3-8570ECDFE3EE")
        and_item.setPos(self.scene.to_scene_point((2, 2)))
        self.scene.addItem(and_item)
        trees = EndpointTrees(None, None)
        ho = GetHightowerObjectAtPoint(self.scene, (0, 0), (10, 10), trees)

        self.assertIs(ho((0, 0)), None)
        self.assertIs(ho((10, 0)), None)

        self.assertIs(ho((2, 2)), hightower.Solid)

    def test_colision_line_tree(self):
        def tsp(x, y):
            return self.scene.to_scene_point((x, y))
        tree_meta = LineTree.metadata_from_path([tsp(5, 0), tsp(15, 0)])
        tree_meta['GUID'] = LineTree.GUI_GUID()
        tree = LineTree(parent=None, metadata=tree_meta)
        self.scene.addItem(tree)
        trees = EndpointTrees(None, None)
        ho = GetHightowerObjectAtPoint(self.scene, (0, 0), (10, 10), trees)

        self.assertIs(ho((0, 0)), None)
        self.assertIs(ho((5, 5)), None)

        self.assertIs(ho((5, 0)), hightower.LineEdge)
        self.assertIs(ho((15, 0)), hightower.LineEdge)

        self.assertIs(ho((10, 0)), hightower.PassableLine)

class TestLineRoute(helpers.CriticalTestCase):
    def setUp(self):
        super().setUp()

        self.app = QtGui.QApplication.instance()
        if not self.app:
            self.app = QtGui.QApplication([])

        self.scene = GridScene()

        # wait until all types have been enumerated
        wait_until_registry_enumerated(self.scene, self.app)

    def tearDown(self):
        # FIXME: No idea why this workaround is necessary :(
        self.scene.deleteLater()
        self.scene._core.quit()
        self.scene._core_thread.join()
        self.scene._registry._registry_handler.quit(True)
        self.scene = None

        self.app.processEvents()

        super().tearDown()

    def test_horizontal_line(self):
        def tsp(x, y):
            return self.scene.to_scene_point((x, y))
        line_route = LineRouteBetweenPoints(self.scene,
                                            tsp(5, 10),
                                            tsp(15, 10))
        line_route.route()
        line_route.do_temp_insert()

        # check line count & temp
        trees = [item for item in self.scene.items() if isinstance(item,
                                                                   LineTree)]
        self.assertEqual(len(trees), 1)
        self.assertTrue(trees[0].is_temporary())

        # insert & check line count & temp
        line_route.do_insert()
        trees = [item for item in self.scene.items() if isinstance(item,
                                                                   LineTree)]
        self.assertEqual(len(trees), 1)
        self.assertFalse(trees[0].is_temporary())

        # check edges
        tree = trees[0]
        self.assertTrue(tree.is_edge(tsp(5, 10)))
        self.assertTrue(tree.is_edge(tsp(15, 10)))

        # contains line
        self.assertTrue(tree.contains_line(QtCore.QLineF(tsp(5, 10),
                                                         tsp(15, 10))))

        self.assertFalse(tree.contains_line(QtCore.QLineF(tsp(5, 10),
                                                          tsp(4, 10))))
        self.assertFalse(tree.contains_line(QtCore.QLineF(tsp(5, 10),
                                                          tsp(5, 9))))
        self.assertFalse(tree.contains_line(QtCore.QLineF(tsp(5, 10),
                                                          tsp(5, 11))))

class TestLineRouteGraphical(helpers.CriticalTestCase):
    def setUp(self):
        super().setUp()

        self.app = QtGui.QApplication.instance()
        if not self.app:
            self.app = QtGui.QApplication([])

        self.scene = GridScene()

        # wait until all types have been enumerated
        wait_until_registry_enumerated(self.scene, self.app)

    def tearDown(self):
        # FIXME: No idea why this workaround is necessary :(
        self.scene.deleteLater()
        self.scene._core.quit()
        self.scene._core_thread.join()
        self.scene._registry._registry_handler.quit(True)
        self.scene = None

        self.app.processEvents()

        super().tearDown()

    def fill_scene(self, scene, input_area):
        lines = input_area.split('\n')

        def add_tree(p1, p2):
            path = [scene.to_scene_point(p1), scene.to_scene_point(p2)]
            metadata = LineTree.metadata_from_path(path)
            l_tree = self.scene.registry().instantiate_frontend_item(
                backend_guid=LineTree.GUI_GUID(),
                additional_metadata=metadata)
            scene.addItem(l_tree)

        # insert line stubs
        for y, line in enumerate(lines):
            for x, char in enumerate(line):
                if char in '-+┬┴':  # horizontal
                    add_tree((x - 0.5, y), (x + 0.5, y))
                if char in '┤+├|':
                    add_tree((x, y - 0.5), (x, y + 0.5))
                if char in '┐˄┬┌x':
                    add_tree((x, y), (x, y + 0.5))
                if char in '┘└˅┴x':
                    add_tree((x, y), (x, y - 0.5))
                if char in '┌└<├x':
                    add_tree((x, y), (x + 0.5, y))
                if char in '>┤┐┘x':
                    add_tree((x, y), (x - 0.5, y))

        # merge trees
        for y, line in enumerate(lines):
            for x, char in enumerate(line):
                for p in [(x, y), (x + 0.5, y), (x, y + 0.5)]:
                    pivot = scene.to_scene_point(p)
                    items = [item for item in scene.items(pivot)
                             if isinstance(item, LineTree)]
                    merger_tree = None
                    for item in items:
                        if not item.is_edge(pivot):
                            continue
                        if merger_tree is None:
                            merger_tree = item
                    if merger_tree is not None:
                        for item in items:
                            if item is not merger_tree:
                                merger_tree.merge_tree(item)
                                scene.removeItem(item)

    def to_positions(self, scene, position_area):
        lines = position_area.split('\n')

        for y, line in enumerate(lines):
            for x, char in enumerate(line):
                if char in 'AX':
                    point_a = x, y
                if char in 'BX':
                    point_b = x, y

        return (scene.to_scene_point(point_a),
                scene.to_scene_point(point_b))

    def verify_output_data(self, scene, output_area):
        lines = output_area.split('\n')

        for y, line in enumerate(lines):
            for x, char in enumerate(line):
                pivot = scene.to_scene_point((x, y))
                items = [item for item in scene.items(pivot)
                         if isinstance(item, LineTree)]
                if char in 'x┴˄˅<┤├>┬-|┘┐┌└':
                    self.assertEqual(len(items), 1, (x, y))
                    self.assertEqual(items[0].is_edge(pivot),
                                     char in 'x┴˄˅<┤├>┬┘┐┌└')
                elif char in ['+']:
                    self.assertLessEqual(len(items), 2)
                    for item in items:
                        self.assertEqual(item.is_edge(pivot), False)
                else:
                    self.assertEqual(len(items), 0)

    def visual_test(self, input_area, position_area, output_area,
                    view_result=False):
        """
        Visual test case definition for inserting lines.

        Visual test cases are defined based on tree areas, basically
        strings defined as a block representing a two-dimensional grid:

        :param input_area: define lines for the starting scene
        :param position_area: define start and endpoint for inserting
        :param output_area: define output lines

        Lines are encoded with unicode characters: ┴˄˅<┤├>┬-|┘┐┌└:
            - horizontal peace, | vertical mid peace
            < left end, > right end, ˄ upper end, ˅ lower end
            + crossing lines, x intersecting lines
            ┤┬ ┘┐┌└ ┴├, corners

        Positions are given as A, B and X:
            A start, B end, X start and end

        When specifying the areas it is advisable to copy the input area,
        to the position area, then adding positions, then copy the result
        to the ouput area and add the added line.

        :param view_result: open scene with resulting grid for debugging

        """
        self.fill_scene(self.scene, input_area)
        start, end = self.to_positions(self.scene, position_area)
        line_route = LineRouteBetweenPoints(self.scene, start, end)
        try:
            line_route.route()
        except RouteNotFoundException:
            pass
        line_route.do_insert()

        if view_result:
            import schematics.mouse_modes
            import sys
            app = QtGui.QApplication.instance()
            if app is None:
                app = QtGui.QApplication(sys.argv)
            view = schematics.EditSchematicView()
            view.setScene(self.scene)
            view.show()
            view.ensureVisible(QtCore.QRectF(0, 0, 0, 0))
            view.setMouseMode(schematics.mouse_modes.InsertLineMode)
            app.exec_()

        self.verify_output_data(self.scene, output_area)

    def test_horizontal_line(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """

        """
        position_area = """

           A             B

        """
        output_area = """

           <------------->

        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_split_line(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """
           <----------->
        """
        position_area = """
           <----A------>

                B
        """
        output_area = """
           <----┬------>
                |
                |
                |
                ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_crossover_line(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """

           <----------->

        """
        position_area = """
                 A
           <----------->
                 B
        """
        output_area = """
                 ˄
           <-----+----->
                 ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_crossover_two_lines(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """

           <----------->
           <----------->
        """
        position_area = """"
                A
           <----------->
           <----B------>
        """
        output_area = """"
                ˄
           <----+------>
           <----┴------>
        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_connected_crossover_two_lines(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """

           ┌----------->
           └----------->
        """
        position_area = """"
                A
           ┌----------->
           └----B------>
        """
        output_area = """"
                ˄
           ┌----+------>
           └----┴------>
        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_route_accross_edge(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """
                 ┌----------->
                 |
           ┌-----┴------------>
           └----------->

        """
        position_area = """
                 ┌-----A----->
                 |
           ┌-----┴------------>
           └----------->
                       B
        """
        output_area = """
                 ┌----┬------>
                 |    |
           ┌-----┴----+------->
           └----------+>
                      └>
        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_connecting_two_lines(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
           ˄        ˄
           |        |
           |        |
           |        |
           ˅        ˅
        """
        position_area = """
           ˄        ˄
           |        |
           A        B
           |        |
           ˅        ˅
        """
        output_area = """
           ˄        ˄
           |        |
           ├--------┤
           |        |
           ˅        ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_extend_over_corner(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
           <--------┐
                    |
                    |
                    |
                    ˅
        """
        position_area = """
           <---A----┐    B
                    |
                    |
                    |
                    ˅
        """
        output_area = """
           <--------┬---->
                    |
                    |
                    |
                    ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_extend_over_corner_swapped(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
           <--------┐
                    |
                    |
                    |
                    ˅
        """
        position_area = """
           <---B----┐    A
                    |
                    |
                    |
                    ˅
        """
        output_area = """
           <--------┬---->
                    |
                    |
                    |
                    ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_extend_over_two_corners(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
           <--------┬-----┐
                    |     |
                    |     |
                    |     |
                    ˅     ˅
        """
        position_area = """
           <---A----┬-----┐     B
                    |     |
                    |     |
                    |     |
                    ˅     ˅
        """
        output_area = """
           <--------┬-----┬----->
                    |     |
                    |     |
                    |     |
                    ˅     ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_connect_passing_crossing_lines(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
              ˄
              |
           <--+-->
              |
              ˅
        """
        position_area = """
              ˄
              |
           <--X-->
              |
              ˅
        """
        output_area = """
              ˄
              |
           <--x-->
              |
              ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_try_route_from_passing_crossing_lines(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
              ˄
              |
           <--+-->
              |
              ˅
        """
        position_area = """
              ˄
              |
        B  <--A-->
              |
              ˅
        """
        output_area = """
              ˄
              |
           <--+-->
              |
              ˅
        """
        self.visual_test(input_area, position_area, output_area)

    def test_simple_route_accross_gap(self):
        # ┴˄˅<┤├>┬-|┘┐┌└
        input_area = """

           <------┐┌----->
                  └┘
        """
        position_area = """

           <---A--┐┌----->    B
                  └┘
        """
        output_area = """
               ┌--------------┐
           <---┴--┐┌----->    ˅
                  └┘
        """
        self.visual_test(input_area, position_area, output_area)

    def test_try_route_loop(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
            ┌-----┐
            |     |
            |     |
            |     |
            ˅     ˅
        """
        position_area = """
            ┌-----┐
            |     |
            A     B
            |     |
            ˅     ˅
        """
        output_area = """
            ┌-----┐
            |     |
            |     |
            |     |
            ˅     ˅
        """
        self.visual_test(input_area, position_area, output_area)

    @expectedFailure
    def test_verifyer(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
            <----->
        """
        position_area = """
            A-----B
        """
        output_area = """
                 ->
        """
        self.visual_test(input_area, position_area, output_area)

    def test_insert_over_edge(self):
        # ┴˄˅<┤├>┬-|┘┐┌└|
        input_area = """
            <----->
        """
        position_area = """
            <----->    B

                A
        """
        output_area = """
            <----->    ˄
                       |
                <------┘
        """
        self.visual_test(input_area, position_area, output_area)


import sys
from collections import deque
sys.stdin = open("./baekjoon/testcase.txt")

N, K = tuple(map(int, sys.stdin.readline().split()))

def BFS(s):
    parent = {s} # set
    frontier = deque([(s, 0)])
    while frontier:
        v1, time1 = frontier.popleft()
        # 한 노드에서 이동할 수 있는 경우 3가지
        next_nodes = [v1+1,v1-1]
        if v1 % 2 == 0:
            next_nodes.append(v1//2)
        for v2 in next_nodes:
            time2 = time1+1
            # 이동 불가능한 지점인지 확인
            if v2 <0:
                continue
            # 이미 방문한 위치인지 확인
            if v2 in parent:
                continue
            # 새로운 위치인 경우, 우선 목표 지점인지 확인
            if v2 == N:
                return time2
            # 새로운 위치이지만 목표 지점이 아닌 경우, 탐색 범위에 추가
            parent.add(v2)
            frontier.append((v2, time2))
    return False

s = K
if s == N:
    print(0)
else:
    time = BFS(s)
    print(time)


# Krishna Pandian

#def findMissingSlow(arr):
  #  for x,y in enumerate(arr):
 #       if x != y:
   #         return x
    #    raise ValueError(msg = "No Missing Element")

def findMissing(arr):
    #print(arr)
    lower = 0               #BST Algorithm
    upper = len(arr) - 1
    middle = (lower + upper) // 2
    while (lower < upper):  #Checks until they lower and upper are equal
        middle = (lower + upper) // 2
        #print("Upper:" + str(upper) + " Lower:" + str(lower) + " Middle:" + str(middle))
        if middle == arr[middle]:
            lower = middle + 1  #increments lower bound to avoid infinite
        else:
             upper = middle

    middle = (lower+upper) // 2
    if arr[middle] != middle:   #checks if a missing element exists
        return middle
    return -1

if __name__ == "__main__":
    #print([1,2,3,4,5])
    arr = list(range(10000000))
    #print(arr)
    arr.remove(3)
    #print(arr)
    print(findMissing(arr))


import pytest
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris

from dabl.utils import data_df_from_bunch
from dabl.plot.utils import find_pretty_grid, plot_coefficients, pairplot

def test_find_pretty_grid():
    # test that the grid is big enough:
    rng = np.random.RandomState(0)
    for i in range(100):
        n_plots = rng.randint(1, 34)
        max_cols = rng.randint(1, 12)
        rows, cols = find_pretty_grid(n_plots=n_plots, max_cols=max_cols)
        assert rows * cols >= n_plots
        assert cols <= max_cols

@pytest.mark.parametrize("n_features, n_top_features",
                         [(5, 10), (10, 5), (10, 40), (40, 10)])
def test_plot_coefficients(n_features, n_top_features):
    coef = np.arange(n_features) + .4
    names = ["feature_{}".format(i) for i in range(n_features)]
    plot_coefficients(coef, names, n_top_features=n_top_features)
    ax = plt.gca()
    assert len(ax.get_xticks()) == min(n_top_features, n_features)
    coef[:-5] = 0
    plot_coefficients(coef, names, n_top_features=n_top_features)
    ax = plt.gca()
    assert len(ax.get_xticks()) == 5

def test_pairplot_iris():
    data = data_df_from_bunch(load_iris())
    axes = pairplot(data, target_col='target')
    # the diagonal axes are duplicated
    # so we can have sharex / sharey
    assert axes.shape == (4, 4)
    assert len(plt.gcf().get_axes()) == 4 * 4 + 4


import streamlit as st
import pandas as pd

FILE_PATH = "salesweekly.csv"

def read_file(FILEPATH):
    return pd.read_csv(FILEPATH)

def main():
    st.title("Here is our homepage")
    df = read_file(FILE_PATH)
    if st.checkbox("show data"):
        st.table(df.head())

if __name__ == "__main__":
    main()


import numpy as np
import os
import time
import scipy.stats as ss
import os
import argparse
import sys
cdir = os.path.dirname(os.path.dirname(sys.path[0]))

np.random.seed(0)
start = time.time()
# parameters for our spin (effective spin,  effective precessing spin) population, which is a multivariate normal distribution + peak
spin_pars = {'mu_eff': 0.06, # mean of effective spin
  'sigma_eff': 0.12, # standard deviation of effective spin
  'mu_p': 0.21, # mean of effective precessing spin
  'sigma_p': 0.09, # standard deviation of effective precessing spin
  'rho': 0.12, # parameter in for the covariance matrix
  'lambda':0.2, # fraction of the peak
  'peak1_mean':0.3,
  'peak2_mean':0.35,
  'peak_sd':0.03}
def TruncNormSampler(clip_a, clip_b, mean, std, Nsamples):
    a, b = (clip_a - mean) / std, (clip_b - mean) / std
    return ss.truncnorm.rvs(a,b,size=Nsamples ) * std + mean

class Gaussian_spin_distribution():
    def __init__(self, peak = False, **model_pars):
        mu_eff = model_pars['mu_eff']
        mu_p = model_pars['mu_p']
        sigma_eff = model_pars['sigma_eff']
        sigma_p = model_pars['sigma_p']
        rho = model_pars['rho']
        self.peak = peak
        self.lambda_peak= model_pars['lambda']
        self.peak1_mean = model_pars['peak1_mean']
        self.peak2_mean = model_pars['peak2_mean']
        self.peak_sd = model_pars['peak_sd']
        self.eff_bounds=[-1,1]
        self.p_bounds=[0,1]

        self.mean = np.array([mu_eff, mu_p])
        self.cov = np.array([[sigma_eff**2, rho*sigma_eff*sigma_p],[rho*sigma_eff*sigma_p,sigma_p**2]])

        self.model=ss.multivariate_normal(mean=self.mean, cov=self.cov)
        self.normalization = [1,1]
#        self.normalization = self.GetNormalization()
#        print(self.normalization)

    def GetGrid(self, n_pts=2000):
        xeff  = np.linspace(self.eff_bounds[0], self.eff_bounds[1], n_pts)
        xp = np.linspace(self.p_bounds[0],self.p_bounds[1],n_pts)
        xeff_grid, xp_grid = np.meshgrid(xeff, xp,indexing='ij')
        return xeff_grid.flatten(), xp_grid.flatten(), xeff, xp
    """
    def GetNormalization(self):
        x,y = self.GetGrid()[:2]
        if self.peak:
            return [2*np.mean(self.model.pdf(np.array([x,y]).T)),2*np.mean(self.narrow_peak_pdf(np.array([x,y])))]
        else:
            return [2*np.mean(self.model.pdf(np.array([x,y]).T))]
    """
    def sample(self, Nsamples):

        if self.peak:
            Npeak = int(Nsamples * self.lambda_peak)
            Nnormal = Nsamples - Npeak
            chi_eff, chi_p = self.model.rvs(size=Nnormal).T
            while True:
                index = np.where((chi_eff>1) + (chi_eff<-1) + (chi_p>1) + (chi_p<0)   )[0]
                n_out = index.size
                if not n_out>0: break
                chi_eff[index], chi_p[index] = self.model.rvs(size=n_out).T
            chi_eff = np.append(chi_eff, self.narrow_peak_rvs(self.peak1_mean, self.peak_sd, size=Npeak) )
            chi_p = np.append(chi_p, self.narrow_peak_rvs(self.peak2_mean, self.peak_sd, size=Npeak) )

        else:
            Nnormal = Nsamples
            chi_eff, chi_p =  self.model.rvs(size=Nnormal).T

            while True:
                index = np.where((chi_eff>1) + (chi_eff<-1) + (chi_p>1) + (chi_p<0)   )[0]
                n_out = index.size
                if not n_out>0: break
                chi_eff[index], chi_p[index] = self.model.rvs(size=n_out).T

        return chi_eff,chi_p
    def narrow_peak_pdf(self, x):
        xeff, xp = x
        p_xeff = np.exp(-(xeff-self.peak1_mean)**2/(2*self.peak_sd**2))/(np.sqrt(2*np.pi)*self.peak_sd)
        p_xp = np.exp(-(xp-self.peak2_mean)**2/(2*self.peak_sd**2))/(np.sqrt(2*np.pi)*self.peak_sd)
        #print(p_xeff, p_xp)
        return p_xeff * p_xp
    def narrow_peak_rvs(self, mean, sd, size):
        return ss.norm.rvs(mean,sd,size)
    """
    def pdf(self, chi_eff, chi_p):
        if self.peak:
            f = self.lambda_peak
            return  (1-f)*self.model.pdf(np.array([chi_eff,chi_p]).T)/self.normalization[0] + f*self.narrow_peak_pdf(np.array([chi_eff,chi_p]) ) / self.normalization[1]
        else:
            return self.model.pdf(np.array([chi_eff,chi_p]).T) / self.normalization
    """
    def pdf(self, chi_eff, chi_p,x=True):
        if self.peak:
            if x:
                f = self.lambda_peak
            else:
                f = 0
            return  (1-f)*self.model.pdf(np.array([chi_eff,chi_p]).T)/self.normalization[0] + f*self.narrow_peak_pdf(np.array([chi_eff,chi_p]) ) / self.normalization[1]
        else:
            return self.model.pdf(np.array([chi_eff,chi_p]).T) / self.normalization

    def marginal_pxeff(self,x):
        f = self.lambda_peak if self.peak else 0
        p1 = np.exp(-(x-self.mean[0])**2/(2*self.cov[0,0]**2))/(np.sqrt(2*np.pi)*self.cov[0,0])
        p2 = np.exp(-(x-self.peak1_mean)**2/(2*self.peak_sd**2))/(np.sqrt(2*np.pi)*self.peak_sd)

        return (1-f)*p1 + f*p2

    def marginal_pxp(self):
        f = self.lambda_peak if self.peak else 0
        p1 = np.exp(-(x-self.mean[1])**2/(2*self.cov[1,1]**2))/(np.sqrt(2*np.pi)*self.cov[1,1])
        p2 = np.exp(-(x-self.peak1_mean)**2/(2*self.peak_sd**2))/(np.sqrt(2*np.pi)*self.peak_sd)

        return (1-f)*p1 + f*p2

def spin_posterior(chi_eff, chi_p, Nevent, posterior_effsigma = 0.2, posterior_psigma = 0.2, Npos = 1000):
    x_eff = []
    x_p = []
    for i in range(Nevent):
        c1 = TruncNormSampler(-1,1,chi_eff[i],posterior_effsigma,1)
        c2 = TruncNormSampler(0,1,chi_p[i],posterior_effsigma,1)
        x_eff.append(TruncNormSampler(-1,1,c1,posterior_effsigma,Npos))
        x_p.append(TruncNormSampler(0,1,c2,posterior_psigma,Npos))
    return np.array(x_eff).reshape(Nevent, Npos), np.array(x_p).reshape(Nevent, Npos)

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Generate population and posterior samples.')
    parser.add_argument("-L", dest = "L", action = 'store_true', help = "Generate lensed population", default = False) # True to turn on the lensing mechanism
    parser.add_argument("-N", type = int, help = "number of population events", default=1986)

    args = parser.parse_args()
    N = args.N # number of binary events

    if args.L:
        savedir = './spin_data_lensed_'+str(N)+'.npz'
    else:
        savedir = './spin_data_unlensed_'+str(N)+'.npz'

    # Initialize the spin population model
    spin_pop = Gaussian_spin_distribution(**spin_pars)

    # Generate the spin catalog
    chi_eff, chi_p = spin_pop.sample(Nsamples=N)

    # generate the posterior
    if args.L: #
        chi_eff = np.concatenate([chi_eff, chi_eff])
        chi_p = np.concatenate([chi_p, chi_p])
        eff_posterior, p_posterior = spin_posterior(chi_eff, chi_p, N*2)
    else:
        eff_posterior, p_posterior = spin_posterior(chi_eff, chi_p, N)

    #np.savez(cdir+'/Mock_Data/spin_data.npz',chi_eff=chi_eff,chi_p=chi_p,eff_posterior=eff_posterior,p_posterior=p_posterior)
    np.savez(savedir,chi_eff=chi_eff,chi_p=chi_p,eff_posterior=eff_posterior,p_posterior=p_posterior)


# Databricks notebook source
# MAGIC %md
# MAGIC # Stateful aggregation alternatives
# MAGIC Scenario: Suppose you would do a running count using stateful streaming aggregations, would there be an alternative option to do this without relying on the state store ?

# COMMAND ----------

# MAGIC %run "./includes/setup"

# COMMAND ----------

import pyspark.sql.types as T
from delta.tables import DeltaTable
from pyspark.sql import DataFrame
import pyspark.sql.functions as F

# Prevent the creation of unnecessary small files.
spark.conf.set("spark.databricks.delta.properties.defaults.autoOptimize.optimizeWrite", True)
spark.conf.set("spark.databricks.delta.properties.defaults.autoOptimize.autoCompact", True)

# Use RocksDB as state store backend.
spark.conf.set("spark.sql.streaming.stateStore.providerClass", "com.databricks.sql.streaming.state.RocksDBStateStoreProvider")

# Clean out old runs.
clean_workspace(user_home)

# COMMAND ----------

# MAGIC %md-sandbox ## Stateful Operator Implementation
# MAGIC
# MAGIC This is roughly what is happening when trying to compute a **groupBy+count** using the stateful API. It will store intermediate counts in the state store and keep joining this to the new data coming in. The number of partitions used in performing the logical join between the new data and existing state will be fixed after stream start (tied to checkpoint), and is tunable using **spark.sql.shuffle.partitions** (default 200).
# MAGIC
# MAGIC

# COMMAND ----------

stateful_output_path = "{}/stateful_aggregation_alternatives/stateful_operator".format(user_home)
stateful_output_checkpoint = "{}/_checkpoint".format(stateful_output_path)

until_stream_is_ready(
    spark
    .readStream
    .format("rate")
    .option("rowsPerSecond", 50)
    .load()
    .withColumn("key", F.col("value") % 50) # Keep generating repeating keys.
    .groupBy("key")
    .count()                                  # This is stateful aggregation relying on the state store. If checkpoint needs to be reset (for whatever reason) -> state gone, needs to be rebuild.
    .writeStream
    .format("delta")
    .outputMode("complete")
    .option("checkpointLocation", stateful_output_checkpoint) # State is stored here.
    .start(stateful_output_path)

)

# COMMAND ----------

# MAGIC %md ### Output

# COMMAND ----------

display(DeltaTable.forPath(spark, stateful_output_path).toDF())

# COMMAND ----------

# MAGIC %md ### Output directory (Delta Lake Table)

# COMMAND ----------

display(dbutils.fs.ls(stateful_output_path))

# COMMAND ----------

# MAGIC %md ### Checkpoint directory

# COMMAND ----------

display(dbutils.fs.ls(stateful_output_checkpoint))

# COMMAND ----------

# MAGIC %md ### State Store files for each of the 200 partitions (output shows partition 0)

# COMMAND ----------

display(dbutils.fs.ls("{}/state/0/0".format(stateful_output_checkpoint)))

# COMMAND ----------

t = DeltaTable.forPath(spark, stateful_output_path)
display(t.history())

# COMMAND ----------

# MAGIC %md-sandbox ## Alternative Implementation (using MERGE instead of Stateful Operators)
# MAGIC
# MAGIC This implementation does not rely on the state store. Instead it MERGEs the input with the already existing output to update the counts.
# MAGIC
# MAGIC

# COMMAND ----------

# MAGIC %md ### Create Delta table that stores state (instead of the state store)

# COMMAND ----------

alternative_output_path = "{}/stateful_aggregation_alternatives/alternative".format(user_home)
alternative_output_checkpoint = "{}/_checkpoint".format(alternative_output_path)

alternative_delta_schema = (
  T.StructType([
    T.StructField("key", T.LongType()),
    T.StructField("count", T.LongType())
])
)

# Create an empty Delta table if it does not exist. This is required for the MERGE to work in the first mini batch.
if not DeltaTable.isDeltaTable(spark, alternative_output_path):
  (
    spark
      .createDataFrame([], alternative_delta_schema)
      .write
      .mode("overwrite")
      .option("overwriteSchema", "true")
      .format("delta")
      .save(alternative_output_path)
  )

# COMMAND ----------

# MAGIC %md ### Define the alternative to groupBy+count

# COMMAND ----------

def group_by_count(updates_df: DataFrame, epoch_id: int):

  # Count the new incoming keys.
  incoming_counts_df = updates_df.groupBy("key").count()

  target_table = DeltaTable.forPath(spark, alternative_output_path)

  # We merge the new counts with the already existing counts.
  (
    target_table.alias("target").merge(
      incoming_counts_df.alias("source"),
      "target.key = source.key")
    .whenMatchedUpdate(set = { "count" : F.col("source.count") + F.col("target.count") } )
    .whenNotMatchedInsertAll()
    .execute()
  )

# COMMAND ----------

# MAGIC %md ### Run the alternative

# COMMAND ----------

until_stream_is_ready(
    spark
    .readStream
    .format("rate")
    .option("rowsPerSecond", 50)
    .load()
    .withColumn("key", F.col("value") % 50) # Keep generating repeating keys.
    .writeStream
    .foreachBatch(group_by_count)
    .outputMode("append")
    .option("checkpointLocation", alternative_output_checkpoint) # Only commits/offsets are stored here, not the actual state of the counts.
    .start(alternative_output_path)
)

# COMMAND ----------

# MAGIC %md ### Alternative Output

# COMMAND ----------

display(DeltaTable.forPath(spark, alternative_output_path).toDF().orderBy("count", ascending=False))

# COMMAND ----------

# MAGIC %md ### Alternative Output directory (Delta Lake Table)

# COMMAND ----------

display(dbutils.fs.ls(alternative_output_path))

# COMMAND ----------

# MAGIC %md ### Alternative Checkpoint directory (see that "state" sub directory is not present)

# COMMAND ----------

display(dbutils.fs.ls(alternative_output_checkpoint))

# COMMAND ----------

# MAGIC %md # Clean up

# COMMAND ----------

for s in spark.streams.active: # Iterate over all active streams
  s.stop()                     # Stop the stream

# Clean out old runs.
clean_workspace(user_home)

# COMMAND ----------

# MAGIC %md # Summary of benefits
# MAGIC
# MAGIC ## Stateful Operator
# MAGIC 1. In-memory backed state store (RocksDB) which can be faster than going to disk.
# MAGIC 2. Code is more concise.
# MAGIC 3. Natively supports dropping of late data.
# MAGIC
# MAGIC ## Alternative
# MAGIC 1. State is queryable as a Delta table.
# MAGIC 2. State will not need to be rebuilt after [making changes that require a wipe of the checkpoint](https://docs.databricks.com/spark/latest/structured-streaming/production.html#recover-after-changes-in-a-streaming-query).
# MAGIC 3. State can grow a lot without requiring a super big cluster (make sure it is manageable though, or you might end up rewriting a lot of data after every mini batch).
# MAGIC 4. Shuffle partitions used in MERGE can be adapted without requiring a checkpoint restrat (compared to them being fixed in the stateful operator implementation).
# MAGIC 4. Can be cheaper as state store backing stateful operator generates a lot of small files, which can incur higher object store / list / put cost.


# Class Objects Attributes and Methods

# when we use mylist. or myset. and it appers bunch of function that we can use
mylist = [1,2,3]
myset = set()
type(myset)
type(mylist)

# NOW, how we can use the class keyword to create a user defined object.
class Sample():
    pass #what is is this doing is basicly to create class and that's it. nothing else there.
my_sample = Sample()
type(my_sample)
""" class is basically a blueprint that defines the nature of a future object from classes.
We can then construct an instance of the object and an instance is a specific object created from a particular class."""

# How to create attribute
class Dog():
    def __init__(self,breed):
        # Attributes
        # We take in the argument
        # Assign it using self.attribute_name
        self.breed = breed

my_dog = Dog(breed='Huskie')
type(my_dog)

my_dog.breed
    # mybreed is was just the parameter name we choose for the argument

# Breakdown what is going on
class Dog():
    def __init__(self,mybreed):

        # Attributes
        # We take in the argument
        # Assign it using self.attribute_name
        self.my_attribute = mybreed

my_dog = Dog(mybreed='Huskie')
type(my_dog)

my_dog.my_attribute

# give it more atribute
class Dog():
    def __init__(self,breed,name,spots):
        # Expecting string
        self.breed = breed
        self.name = name
        # Expect boolean True/False
        self.spots = spots

my_dog = Dog(breed="lab",name='sammy',spots=False)
my_dog.breed


import threading, sys, os
import json
import numpy as np

from functools import partial

from neuron.accuracy import mse
from neuron.neuron import BaseNeuron
from neuron.activations import limiar
from neuron.data_transformation import dict_transf_funcs

from graphs.classification_plot import PygameClassifierPlot

def train_neuron(neuron, screen, n_iterations, train_x, train_y):
    screen.add_prediction_points(train_x, train_y)

    for i in range(n_iterations):
        pred = []
        for x, y in zip(train_x, train_y):
            neuron.train_step(x, y)
        for x in train_x:
            pred.append(neuron.output(x))

        screen.update_screen_predictions()

        print(neuron.log(train_x, train_y))

        if mse(train_y, pred) == 0:
            print(f"pesos : {neuron._weights}")
            print(f"previsões : {pred}")
            break

    print("terminou")
    q = input()
    os._exit(1)

def pygame_classifier_plot(train_x, train_y, transform_func):
    def limiar_0_5(value):
        return limiar(value, 0.5)

    n_neurons = len(train_x[0])
    neuron = BaseNeuron(n_neurons, limiar_0_5, transform_func)
    screen = PygameClassifierPlot(neuron)

    thread = threading.Thread(target = train_neuron, args = (neuron, screen, 30, train_x, train_y))
    thread.start()

    screen.run()

if __name__ == "__main__":
    args = sys.argv
    file_name = args[1]
    input_file = json.load(open(f"dados/{file_name}", "r"))
    train_x = input_file["inputs"]
    train_y = input_file["outputs"]

    if len(args) == 3:
        transform_func = args[2]
        transform_func = dict_transf_funcs[transform_func]
    else:
        transform_func = lambda x : x

    pygame_classifier_plot(train_x, train_y, transform_func)

Atividades disciplinas/6 periodo/IA/rede neurais/main.py


import argparse
import os
import sys
import logging
import traceback

import geopandas as gpd

def get_parser():
    """
    Parser from command line arguments

    :return myparser: Parser
    :rtype: ArgumentParser

    """
    myparser = argparse.ArgumentParser(os.path.basename(__file__))
    myparser.add_argument("-i", type=str, required=True,
                          help="Water mask file")
    myparser.add_argument("-o", type=str, required=True,
                          help="Output file")
    myparser.add_argument("-v", type=float, required=True,
                          help="Height value")
    return myparser

def process_file(inputfile: str, outputfile: str, height: float):
    '''
    Change height of water mask for swot-hydrology-toolbox

    :param inputfile: Input file
    :param outputfile: Output file
    '''

    try:
        data = gpd.read_file(inputfile)
        data.at[0,'height'] = height
        data.to_file(outputfile)
    except:
        traceback.print_exc()
        logging.error(f"Writing output file")
    else:
        logging.info("Write output file: OK")

# Main program
if __name__ == "__main__":
    parser = get_parser()
    args = parser.parse_args()
    level = getattr(logging, "INFO")
    logging.basicConfig(filename=None,format='%(asctime)s [%(levelname)s] %(message)s', level=level)
    process_file(args.i, args.o, args.v)

# End


#!/usr/bin/python3
#import ipdb
#import helper

def nextPalandrom(x):
    x_int=int(x)
    x_new=x_int-1
    while(True):
        if str(x_new)[::-1]==str(x_new):
            return str(x_new)
        else:
            x_new-=1

def convert(n,b):
    n_str=str(n)
    ans=''
    for i in range(10,-1,-1):
        ans+=str(int(n/b**i))
        if n%b**i==0:
            if i>0:
                for p in range(i):
                    ans+='0'
            break
        else:
            n=n%b**i
    return(str(int(ans)))

ans=0
N,b=input().split()
N=int(N)
b=int(b)
#N=int(input())
for i in range(N,0,-1):
    i_str=str(i)
    if i_str==i_str[::-1]:
        bb=convert(i,b)
        if bb==bb[::-1] :
            ans+=i
print(ans)