from flask import g, session
from sqlalchemy import DDL
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.event import listens_for
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import column_property, mapper
from sqlalchemy.orm.base import NEVER_SET, NO_VALUE
from sqlalchemy.sql import select

from indico.core.db import db
from indico.core.db.sqlalchemy.attachments import AttachedItemsMixin
from indico.core.db.sqlalchemy.descriptions import RenderMode, SearchableDescriptionMixin
from indico.core.db.sqlalchemy.locations import LocationMixin
from indico.core.db.sqlalchemy.notes import AttachedNotesMixin
from indico.core.db.sqlalchemy.protection import ProtectionManagersMixin, ProtectionMode
from indico.core.db.sqlalchemy.searchable import SearchableTitleMixin
from indico.core.db.sqlalchemy.util.models import auto_table_args
from indico.core.db.sqlalchemy.util.queries import increment_and_get
from indico.core.db.sqlalchemy.util.session import no_autoflush
from indico.modules.events.editing.models.editable import EditableType
from indico.modules.events.management.util import get_non_inheriting_objects
from indico.modules.events.models.events import Event
from indico.modules.events.models.persons import AuthorsSpeakersMixin, PersonLinkMixin
from indico.modules.events.papers.models.papers import Paper
from indico.modules.events.papers.models.revisions import PaperRevision, PaperRevisionState
from indico.modules.events.sessions.models.sessions import Session
from indico.modules.events.sessions.util import session_coordinator_priv_enabled
from indico.util.iterables import materialize_iterable
from indico.util.locators import locator_property
from indico.util.string import format_repr, slugify

def _get_next_friendly_id(context):
    """Get the next friendly id for a contribution."""
    from indico.modules.events import Event
    event_id = context.current_parameters['event_id']

    # Check first if there is a pre-allocated friendly id
    # (and use it in that case)
    friendly_ids = g.get('friendly_ids', {}).get(Contribution, {}).get(event_id, [])
    if friendly_ids:
        return friendly_ids.pop(0)

    assert event_id is not None
    return increment_and_get(Event._last_friendly_contribution_id, Event.id == event_id)

class CustomFieldsMixin:
    """Methods to process custom field data."""

    def get_field_value(self, field_id, raw=False):
        fv = next((v for v in self.field_values if v.contribution_field_id == field_id), None)
        if raw:
            return fv
        else:
            return fv.friendly_data if fv else ''

    @no_autoflush
    def set_custom_field(self, field_id, field_value):
        fv = self.get_field_value(field_id, raw=True)
        if not fv:
            field_value_cls = type(self).field_values.prop.mapper.class_
            fv = field_value_cls(contribution_field=self.event.get_contribution_field(field_id))
            self.field_values.append(fv)
        old_value = fv.data
        fv.data = field_value
        return old_value

class Contribution(SearchableTitleMixin, SearchableDescriptionMixin, ProtectionManagersMixin, LocationMixin,
                   AttachedItemsMixin, AttachedNotesMixin, PersonLinkMixin, AuthorsSpeakersMixin, CustomFieldsMixin,
                   db.Model):
    __tablename__ = 'contributions'
    __auto_table_args = (db.Index(None, 'friendly_id', 'event_id', unique=True,
                                  postgresql_where=db.text('NOT is_deleted')),
                         db.Index(None, 'event_id', 'track_id'),
                         db.Index(None, 'event_id', 'abstract_id'),
                         db.Index(None, 'abstract_id', unique=True, postgresql_where=db.text('NOT is_deleted')),
                         db.CheckConstraint('session_block_id IS NULL OR session_id IS NOT NULL',
                                            'session_block_if_session'),
                         db.CheckConstraint("date_trunc('minute', duration) = duration", 'duration_no_seconds'),
                         db.ForeignKeyConstraint(['session_block_id', 'session_id'],
                                                 ['events.session_blocks.id', 'events.session_blocks.session_id']),
                         {'schema': 'events'})
    person_link_relation_name = 'ContributionPersonLink'
    person_link_backref_name = 'contribution'
    location_backref_name = 'contributions'
    disallowed_protection_modes = frozenset()
    inheriting_have_acl = True
    possible_render_modes = {RenderMode.html, RenderMode.markdown}
    default_render_mode = RenderMode.markdown
    allow_relationship_preloading = True

    PRELOAD_EVENT_ATTACHED_ITEMS = True
    PRELOAD_EVENT_NOTES = True
    ATTACHMENT_FOLDER_ID_COLUMN = 'contribution_id'

    @classmethod
    def allocate_friendly_ids(cls, event, n):
        """Allocate n Contribution friendly_ids.

        This is needed so that we can allocate all IDs in one go. Not doing
        so could result in DB deadlocks. All operations that create more than
        one contribution should use this method.

        :param event: the :class:`Event` in question
        :param n: the number of ids to pre-allocate
        """
        from indico.modules.events import Event
        fid = increment_and_get(Event._last_friendly_contribution_id, Event.id == event.id, n)
        friendly_ids = g.setdefault('friendly_ids', {})
        friendly_ids.setdefault(cls, {})[event.id] = list(range(fid - n + 1, fid + 1))

    @declared_attr
    def __table_args__(cls):
        return auto_table_args(cls)

    id = db.Column(
        db.Integer,
        primary_key=True
    )
    #: The human-friendly ID for the contribution
    friendly_id = db.Column(
        db.Integer,
        nullable=False,
        default=_get_next_friendly_id
    )
    event_id = db.Column(
        db.Integer,
        db.ForeignKey('events.events.id'),
        index=True,
        nullable=False
    )
    session_id = db.Column(
        db.Integer,
        db.ForeignKey('events.sessions.id'),
        index=True,
        nullable=True
    )
    session_block_id = db.Column(
        db.Integer,
        db.ForeignKey('events.session_blocks.id'),
        index=True,
        nullable=True
    )
    track_id = db.Column(
        db.Integer,
        db.ForeignKey('events.tracks.id', ondelete='SET NULL'),
        index=True,
        nullable=True
    )
    abstract_id = db.Column(
        db.Integer,
        db.ForeignKey('event_abstracts.abstracts.id'),
        index=True,
        nullable=True
    )
    type_id = db.Column(
        db.Integer,
        db.ForeignKey('events.contribution_types.id'),
        index=True,
        nullable=True
    )
    code = db.Column(
        db.String,
        nullable=False,
        default=''
    )
    duration = db.Column(
        db.Interval,
        nullable=False
    )
    board_number = db.Column(
        db.String,
        nullable=False,
        default=''
    )
    keywords = db.Column(
        ARRAY(db.String),
        nullable=False,
        default=[]
    )
    is_deleted = db.Column(
        db.Boolean,
        nullable=False,
        default=False
    )
    #: The last user-friendly sub-contribution ID
    _last_friendly_subcontribution_id = db.deferred(db.Column(
        'last_friendly_subcontribution_id',
        db.Integer,
        nullable=False,
        default=0
    ))

    event = db.relationship(
        'Event',
        lazy=True,
        backref=db.backref(
            'contributions',
            primaryjoin='(Contribution.event_id == Event.id) & ~Contribution.is_deleted',
            cascade='all, delete-orphan',
            lazy=True
        )
    )
    session = db.relationship(
        'Session',
        lazy=True,
        backref=db.backref(
            'contributions',
            primaryjoin='(Contribution.session_id == Session.id) & ~Contribution.is_deleted',
            lazy=True
        )
    )
    session_block = db.relationship(
        'SessionBlock',
        lazy=True,
        foreign_keys=[session_block_id],
        backref=db.backref(
            'contributions',
            primaryjoin='(Contribution.session_block_id == SessionBlock.id) & ~Contribution.is_deleted',
            lazy=True
        )
    )
    type = db.relationship(
        'ContributionType',
        lazy=True,
        backref=db.backref(
            'contributions',
            lazy=True
        )
    )
    acl_entries = db.relationship(
        'ContributionPrincipal',
        lazy=True,
        cascade='all, delete-orphan',
        collection_class=set,
        backref='contribution'
    )
    subcontributions = db.relationship(
        'SubContribution',
        lazy=True,
        primaryjoin='(SubContribution.contribution_id == Contribution.id) & ~SubContribution.is_deleted',
        order_by='SubContribution.position',
        cascade='all, delete-orphan',
        backref=db.backref(
            'contribution',
            primaryjoin='SubContribution.contribution_id == Contribution.id',
            lazy=True
        )
    )
    abstract = db.relationship(
        'Abstract',
        lazy=True,
        backref=db.backref(
            'contribution',
            primaryjoin='(Contribution.abstract_id == Abstract.id) & ~Contribution.is_deleted',
            lazy=True,
            uselist=False
        )
    )
    track = db.relationship(
        'Track',
        lazy=True,
        backref=db.backref(
            'contributions',
            primaryjoin='(Contribution.track_id == Track.id) & ~Contribution.is_deleted',
            lazy=True,
            passive_deletes=True
        )
    )
    #: External references associated with this contribution
    references = db.relationship(
        'ContributionReference',
        lazy=True,
        cascade='all, delete-orphan',
        backref=db.backref(
            'contribution',
            lazy=True
        )
    )
    #: Data stored in abstract/contribution fields
    field_values = db.relationship(
        'ContributionFieldValue',
        lazy=True,
        cascade='all, delete-orphan',
        backref=db.backref(
            'contribution',
            lazy=True
        )
    )
    #: The accepted paper revision
    _accepted_paper_revision = db.relationship(
        'PaperRevision',
        lazy=True,
        viewonly=True,
        uselist=False,
        primaryjoin=lambda: db.and_(PaperRevision._contribution_id == Contribution.id,
                                    PaperRevision.state == PaperRevisionState.accepted),
    )
    #: Paper files not submitted for reviewing
    pending_paper_files = db.relationship(
        'PaperFile',
        lazy=True,
        viewonly=True,
        primaryjoin='(PaperFile._contribution_id == Contribution.id) & (PaperFile.revision_id.is_(None))',
    )
    #: Paper reviewing judges
    paper_judges = db.relationship(
        'User',
        secondary='event_paper_reviewing.judges',
        collection_class=set,
        lazy=True,
        backref=db.backref(
            'judge_for_contributions',
            collection_class=set,
            lazy=True
        )
    )
    #: Paper content reviewers
    paper_content_reviewers = db.relationship(
        'User',
        secondary='event_paper_reviewing.content_reviewers',
        collection_class=set,
        lazy=True,
        backref=db.backref(
            'content_reviewer_for_contributions',
            collection_class=set,
            lazy=True
        )
    )
    #: Paper layout reviewers
    paper_layout_reviewers = db.relationship(
        'User',
        secondary='event_paper_reviewing.layout_reviewers',
        collection_class=set,
        lazy=True,
        backref=db.backref(
            'layout_reviewer_for_contributions',
            collection_class=set,
            lazy=True
        )
    )

    @declared_attr
    def _paper_last_revision(cls):
        # Incompatible with joinedload
        subquery = (db.select([db.func.max(PaperRevision.submitted_dt)])
                    .where(PaperRevision._contribution_id == cls.id)
                    .correlate_except(PaperRevision)
                    .scalar_subquery())
        return db.relationship(
            'PaperRevision',
            uselist=False,
            lazy=True,
            viewonly=True,
            primaryjoin=db.and_(PaperRevision._contribution_id == cls.id, PaperRevision.submitted_dt == subquery)
        )

    # relationship backrefs:
    # - _paper_files (PaperFile._contribution)
    # - _paper_revisions (PaperRevision._contribution)
    # - attachment_folders (AttachmentFolder.contribution)
    # - editables (Editable.contribution)
    # - legacy_mapping (LegacyContributionMapping.contribution)
    # - note (EventNote.contribution)
    # - room_reservation_links (ReservationLink.contribution)
    # - timetable_entry (TimetableEntry.contribution)
    # - vc_room_associations (VCRoomEventAssociation.linked_contrib)

    @declared_attr
    def is_scheduled(cls):
        from indico.modules.events.timetable.models.entries import TimetableEntry
        query = (db.exists([1])
                 .where(TimetableEntry.contribution_id == cls.id)
                 .correlate_except(TimetableEntry))
        return db.column_property(query, deferred=True)

    @declared_attr
    def subcontribution_count(cls):
        from indico.modules.events.contributions.models.subcontributions import SubContribution
        query = (db.select([db.func.count(SubContribution.id)])
                 .where((SubContribution.contribution_id == cls.id) & ~SubContribution.is_deleted)
                 .correlate_except(SubContribution)
                 .scalar_subquery())
        return db.column_property(query, deferred=True)

    @declared_attr
    def _paper_revision_count(cls):
        query = (db.select([db.func.count(PaperRevision.id)])
                 .where(PaperRevision._contribution_id == cls.id)
                 .correlate_except(PaperRevision)
                 .scalar_subquery())
        return db.column_property(query, deferred=True)

    def __init__(self, **kwargs):
        # explicitly initialize those relationships with None to avoid
        # an extra query to check whether there is an object associated
        # when assigning a new one (e.g. during cloning)
        kwargs.setdefault('note', None)
        kwargs.setdefault('timetable_entry', None)
        super().__init__(**kwargs)

    @classmethod
    def preload_acl_entries(cls, event):
        cls.preload_relationships(cls.query.with_parent(event), 'acl_entries')

    @property
    def location_parent(self):
        if self.session_block_id is not None:
            return self.session_block
        elif self.session_id is not None:
            return self.session
        else:
            return self.event

    @property
    def protection_parent(self):
        return self.session if self.session_id is not None else self.event

    @property
    def start_dt(self):
        return self.timetable_entry.start_dt if self.timetable_entry else None

    @property
    def end_dt(self):
        return self.timetable_entry.start_dt + self.duration if self.timetable_entry else None

    @property
    def start_dt_poster(self):
        if self.session and self.session.is_poster and self.timetable_entry and self.timetable_entry.parent:
            return self.timetable_entry.parent.start_dt

    @property
    def end_dt_poster(self):
        if self.session and self.session.is_poster and self.timetable_entry and self.timetable_entry.parent:
            return self.timetable_entry.parent.end_dt

    @property
    def duration_poster(self):
        if self.session and self.session.is_poster and self.timetable_entry and self.timetable_entry.parent:
            return self.timetable_entry.parent.duration

    @property
    def start_dt_display(self):
        """The displayed start time of the contribution.

        This is the start time of the poster session if applicable,
        otherwise the start time of the contribution itself.
        """
        return self.start_dt_poster or self.start_dt

    @property
    def end_dt_display(self):
        """The displayed end time of the contribution.

        This is the end time of the poster session if applicable,
        otherwise the end time of the contribution itself.
        """
        return self.end_dt_poster or self.end_dt

    @property
    def duration_display(self):
        """The displayed duration of the contribution.

        This is the duration of the poster session if applicable,
        otherwise the duration of the contribution itself.
        """
        return self.duration_poster or self.duration

    @property
    def submitters(self):
        return {person_link for person_link in self.person_links if person_link.is_submitter}

    @locator_property
    def locator(self):
        return dict(self.event.locator, contrib_id=self.id)

    @property
    def verbose_title(self):
        return f'#{self.friendly_id} ({self.title})'

    @property
    def paper(self):
        return Paper(self) if self._paper_last_revision else None

    @property
    def allowed_types_for_editable(self):
        from indico.modules.events.editing.settings import editable_type_settings
        if not self.event.has_feature('editing'):
            return []

        submitted_for = {editable.type.name for editable in self.editables}
        return [
            editable_type
            for editable_type in self.event.editable_types
            if editable_type not in submitted_for
            and editable_type_settings[EditableType[editable_type]].get(self.event, 'submission_enabled')
        ]

    @property
    def enabled_editables(self):
        """Return all submitted editables with enabled types."""
        from indico.modules.events.editing.settings import editing_settings
        if not self.event.has_feature('editing'):
            return []

        enabled_editable_types = editing_settings.get(self.event, 'editable_types')
        enabled_editables = [editable for editable in self.editables if editable.type.name in enabled_editable_types]
        order = list(EditableType)
        return sorted(enabled_editables, key=lambda editable: order.index(editable.type))

    @property
    def has_published_editables(self):
        return any(e.published_revision_id is not None for e in self.enabled_editables)

    @property
    def slug(self):
        return slugify(self.friendly_id, self.title, maxlen=30)

    def is_paper_reviewer(self, user):
        return user in self.paper_content_reviewers or user in self.paper_layout_reviewers

    def __repr__(self):
        return format_repr(self, 'id', is_deleted=False, _text=self.title)

    def can_manage(self, user, permission=None, allow_admin=True, check_parent=True, explicit_permission=False):
        if super().can_manage(user, permission, allow_admin=allow_admin, check_parent=check_parent,
                              explicit_permission=explicit_permission):
            return True
        if (check_parent and self.session_id is not None and
                self.session.can_manage(user, 'coordinate', allow_admin=allow_admin,
                                        explicit_permission=explicit_permission) and
                session_coordinator_priv_enabled(self.event, 'manage-contributions')):
            return True
        return False

    def can_edit(self, user):
        # Submitters can edit their own contributions if configured
        from indico.modules.events.contributions import contribution_settings
        submitters_can_edit = contribution_settings.get(self.event, 'submitters_can_edit')
        return self.can_manage(user, permission=('submit' if submitters_can_edit else None))

    @materialize_iterable()
    def get_manage_button_options(self, *, note_may_exist=False):
        if self.event.is_locked:
            return
        if self.can_edit_note(session.user) and (note_may_exist or not self.has_note):
            yield 'notes_edit'
        if self.can_edit(session.user):
            yield 'contribution_edit'
        if self.can_manage(session.user):
            yield 'contribution_protection_edit'
        if self.can_manage_attachments(session.user):
            yield 'attachments_edit'
        if self.can_manage(session.user):
            yield 'contribution_subcontributions_edit'

    def get_non_inheriting_objects(self):
        """Get a set of child objects that do not inherit protection."""
        return get_non_inheriting_objects(self)

    def is_user_associated(self, user, check_abstract=False):
        if user is None:
            return False
        if check_abstract and self.abstract and self.abstract.submitter == user:
            return True
        return any(pl.person.user == user for pl in self.person_links if pl.person.user)

    def can_submit_proceedings(self, user):
        """Whether the user can submit editables/papers."""
        if user is None:
            return False
        # The submitter of the original abstract is always authorized
        if self.abstract and self.abstract.submitter == user:
            return True
        # Otherwise only users with submission rights are authorized
        return self.can_manage(user, 'submit', allow_admin=False, check_parent=False)

    def get_editable(self, editable_type):
        """Get the editable of the given type."""
        return next((e for e in self.editables if e.type == editable_type), None)

    def log(self, *args, **kwargs):
        """Log with prefilled metadata for the contribution."""
        return self.event.log(*args, meta={'contribution_id': self.id}, **kwargs)

Contribution.register_protection_events()

@listens_for(mapper, 'after_configured', once=True)
def _mapper_configured():
    # Contribution.effective_protection_mode -- the effective protection mode
    # (public/protected) of the contribution, even if it's inheriting it from
    # its event or session
    protection_mode_case = db.case([
        ((Contribution.protection_mode == ProtectionMode.inheriting) & Contribution.session_id.is_(None),
         Event.effective_protection_mode),
        ((Contribution.protection_mode == ProtectionMode.inheriting) & Contribution.session_id.isnot(None),
         select([Session.effective_protection_mode])
         .where(Session.id == Contribution.session_id)
         .correlate(Contribution)
         .scalar_subquery())
    ], else_=Contribution.protection_mode)
    query = (
        select([protection_mode_case])
        .where(Event.id == Contribution.event_id)
        .correlate(Contribution)
        .scalar_subquery()
    )
    Contribution.effective_protection_mode = column_property(query, deferred=True)

    Contribution.register_location_events()

    @listens_for(Contribution.session, 'set')
    def _set_session_block(target, value, *unused):
        if value is None:
            target.session_block = None

    @listens_for(Contribution.timetable_entry, 'set')
    @no_autoflush
    def _set_timetable_entry(target, value, *unused):
        if value is None:
            target.session_block = None
        else:
            if target.session is not None:
                target.session_block = value.parent.session_block

    @listens_for(Contribution.duration, 'set')
    def _set_duration(target, value, oldvalue, *unused):
        from indico.modules.events.util import register_time_change
        if oldvalue in (NEVER_SET, NO_VALUE):
            return
        if value != oldvalue and target.timetable_entry is not None:
            register_time_change(target.timetable_entry)

@listens_for(Contribution.__table__, 'after_create')
def _add_timetable_consistency_trigger(target, conn, **kw):
    sql = '''
        CREATE CONSTRAINT TRIGGER consistent_timetable
        AFTER INSERT OR UPDATE OF event_id, session_id, session_block_id, duration
        ON {}
        DEFERRABLE INITIALLY DEFERRED
        FOR EACH ROW
        EXECUTE PROCEDURE events.check_timetable_consistency('contribution');
    '''.format(target.fullname)
    DDL(sql).execute(conn)


from mpi4py import MPI
import numpy as np
import adios2

# MPI
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()

# User data
Nx = 10
Ny = 10

count = [Nx, Ny]
start = [rank * Nx, 0]
shape = [size * Nx, Ny]

temperatures = np.zeros(count, dtype=np.int32)

for i in range(0, Nx):
    for j in range(0, Ny):
        temperatures[i, j] = (start[0] + i) * shape[1] + (j + start[1])

# ADIOS write
adios = adios2.ADIOS(comm)
ioWrite = adios.DeclareIO("ioWriter")

varTemperature = ioWrite.DefineVariable("temperature2D", temperatures, shape,
                                        start, count, adios2.ConstantDims)
ioWrite.SetEngine("NULL")

nullWriter = ioWrite.Open('NULL_py.bp', adios2.Mode.Write)

assert (nullWriter.Type() == "NullWriter")

status = nullWriter.BeginStep()
assert (status == adios2.StepStatus.OK)

nullWriter.Put(varTemperature, temperatures)
nullWriter.EndStep()
nullWriter.Close()

# ADIOS2 read
ioRead = adios.DeclareIO("ioReader")
ioRead.SetEngine("null")
nullReader = ioRead.Open('NULL_py.bp', adios2.Mode.Read, MPI.COMM_SELF)

assert (nullReader.Type() == "NullReader")

inTemperatures = np.zeros(1, dtype=np.int32)

status = nullReader.BeginStep()
assert (status == adios2.StepStatus.EndOfStream)

var_inTemperature = ioRead.InquireVariable("temperature2D")

if (var_inTemperature is True):
    raise ValueError('var_inTemperature is not False')

# nullReader.Get(var_inTemperature, inTemperatures)

nullReader.PerformGets()
nullReader.EndStep()
nullReader.Close()


# Import Dependencies
from flask import Flask, render_template, redirect
from flask_pymongo import PyMongo
import pymongo
import Mars_Scraped
import os

app = Flask(__name__)

# Create connection variable
conn = 'mongodb://localhost:27017/Mars_App'

# Pass connection to the pymongo instance.
client = pymongo.MongoClient(conn)

# Connect to a database.
db = client.Mars_App

# Create route that renders index.html template and finds documents from mongo
@app.route("/")
def index():

    # Find data
    App_info = dict(db.Mars_Data.__doc__)

    # Return template and data
    print(App_info)
    return render_template("index.html", mars_info=App_info)

# Route that will trigger scrape function
@app.route("/scrape")
def scrape():

    Mars_Scraped.scrape(update_db=True, update_scrape='All')

    return redirect("/", code=302)

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


import numpy as np
import cv2
import glob

def calibrate_camera(chessboard_size, square_size, images_path):
    objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
    objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
    objp *= square_size

    objpoints = []
    imgpoints = []

    images = glob.glob(images_path)

    gray = None

    for fname in images:
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # 체스 보드 코너를 찾자
        ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

        if ret == True:
            objpoints.append(objp)
            corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
            imgpoints.append(corners2)
            img = cv2.drawChessboardCorners(img, chessboard_size, corners2, ret)
            cv2.imshow('img', img)
            cv2.waitKey(200)

    cv2.destroyAllWindows()
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

    return ret, mtx, dist

def project_points(points_3d, rvecs, tvecs, camera_matrix):
    R, _ = cv2.Rodrigues(rvecs) # 회전 벡터 -> 회전 행렬
    RT = np.concatenate((R, tvecs), axis=1) # 외부 매개변수 행렬
    P = np.matmul(camera_matrix, RT) # 투영 행렬

    points_3d_homogeneous = np.concatenate((points_3d, np.ones((points_3d.shape[0], 1))), axis=1) # 동차 좌표로 변환
    points_2d = np.matmul(P, points_3d_homogeneous.T).T # 평면에 투영

    # 정규화한다
    for i in range(points_2d.shape[0]):
        points_2d[i, 0] /= points_2d[i, 2]
        points_2d[i, 1] /= points_2d[i, 2]
        points_2d[i, 2] = 1

    return points_2d[:, :2]

def draw_cube(img, imgpts):
    imgpts = np.int32(imgpts).reshape(-1,2)
    color = (255, 0, 0)
    lines = [(0,1), (1,2), (2,3), (3,0), (0,4), (1,5), (2,6), (3,7), (4,5), (5,6), (6,7), (7,4)]

    for i, j in lines:
        img = cv2.line(img, tuple(imgpts[i]), tuple(imgpts[j]), color, 3)

    return img

# 카메라 캘리브레이션 설정
# camera_index = cv2.CAP_DSHOW # 노트북 웹캠
camera_index = cv2.CAP_DSHOW + 1 # USB 웹캠
chessboard_size = (4,4)
square_size = 1.0
calibration_images_path = 'calibration_images/*.jpg'  # 캘리브레이션 이미지 경로

ret, mtx, dist = calibrate_camera(chessboard_size, square_size, calibration_images_path)

# 웹캠 준비
camera = cv2.VideoCapture(camera_index)

while(True):
    # 캡처
    ret, frame = camera.read()

    if ret == True:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 체스보드 코너 찾기
        ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

        if ret == True:
            objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
            objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
            objp *= square_size

            # 큐브 꼭지점
            axis = np.float32([[0,0,0], [1,0,0], [1,1,0], [0,1,0],
                               [0,0,-1],[1,0,-1],[1,1,-1],[0,1,-1]])

            # 회전 및 변환 벡터를 계산
            _, rvecs, tvecs = cv2.solvePnP(objp, corners, mtx, dist)

            # 큐브의 꼭지점을 평면에 투영
            imgpts = project_points(axis, rvecs, tvecs, mtx)

            frame = draw_cube(frame, imgpts)

        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

camera.release()
cv2.destroyAllWindows()


import numpy as np
import cv2

# input image array
img = np.array([[3, 4, 8, 1, 7],
                [9, 4, 2, 1, 6],
                [7, 8, 8, 1, 1]], np.uint8)

# kernel array
kernel = np.array([[0, 1, 0],
                   [1, 1, 0],
                   [0, 1, 0]], np.uint8)

# performing closing using dilation followed by erosion
dilation = cv2.dilate(img,kernel,iterations = 1)
erosion = cv2.erode(dilation,kernel,iterations = 1)

# performing closing using its function
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

print(erosion)
print(closing)
print(erosion==closing)

# Conclusion
# Closing is just another way of saying dilation followed by erosion


'''
В этой задаче вам нужно написать программу, которая будет выполнять следующий сценарий:

Открыть страницу http://suninjuly.github.io/explicit_wait2.html
Дождаться, когда цена дома уменьшится до $100 (ожидание нужно установить не меньше 12 секунд)
Нажать на кнопку "Book"
Решить уже известную нам математическую задачу (используйте ранее написанный код) и отправить решение
'''
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import math

link = r"http://suninjuly.github.io/explicit_wait2.html"
try:
    bro = webdriver.Chrome()
    bro.get(link)
    # easy method to wait - browser.implicitly_wait(5)
    price = WebDriverWait(bro, 12).until(EC.text_to_be_present_in_element((By.ID, "price"), "100"))
    bro.find_element_by_css_selector("button#book").click()
    x_val = bro.find_element_by_id("input_value")
    res = str(math.log(abs(12 * math.sin(int(x_val.text)))))
    bro.find_element_by_id("answer").send_keys(res)
    bro.find_element_by_id("solve").click()
    al = bro.switch_to.alert
    print(al.text)  # find answer for task
    al.accept()

finally:
    bro.quit()


from os import abort
from pymongo import MongoClient

def print_results(values):
    print("{0} | {1} | {2} | {3}".format(values[0], values[1], values[2], values[3]))

# creates an index based on the field passed in and puts it in ascending order.
def create_index(host, port, udb, ucollection, field):
    connection = MongoClient(host, port)
    db = connection[udb]
    collection = db[ucollection]

    collection.create_index(field)
    print('Index created for AuctionID...')

def find_bid(host, port, udb, ucollection, bid_id):
    connection = MongoClient(host, port)
    db = connection[udb]
    collection = db[ucollection]

    pipeline = [
        {"$match": {"AuctionID": bid_id}},
        {"$project": {"AuctionID": 1, "AuctionTitle": 1, "Fund": 1, "AuctionFeeTotal": 1, "_id": 0}}
    ]

    # Convert the pipeline values to a printable format
    cursor = collection.aggregate(pipeline)
    results = list(cursor)

    if not results:
        print("No bid found....")
        return None

    # Pull the dictionary object out of the list
    results_string = results[0]
    listed_values = []

    for i in results_string:
        value = results_string[i]
        listed_values.append(value)

    return listed_values

def create_bid(host, port, udb, ucollection):
    connection = MongoClient(host, port)
    db = connection[udb]
    collection = db[ucollection]

    # Counter for the check_bid below to only run for the first iteration
    counter = 0

    field_list = ["AuctionID", "AuctionTitle", "Fund", "AuctionFeeTotal"]
    new_bid = []

    for field in field_list:
        user_value = input("Enter {0}:  ".format(field))
        new_bid.append(user_value)

        if counter is 0:
            # Check if bid exists with AuctionID and stop creation if it does.
            check_bid = find_bid(host, port, udb, ucollection, int(user_value))
            counter += 1

        if check_bid:
            print("Bid with that AuctionID already exists...")
            return None

    bid = {"AuctionID": int(new_bid[0]),
           "AuctionTitle": new_bid[1],
           "Fund": new_bid[2],
           "AuctionFeeTotal": new_bid[3]}

    try:
        collection.insert_one(bid).inserted_id
        find_bid(host, port, udb, ucollection, int(new_bid[0]))
    except:
        abort(400, str("Error in process. Aborted."))

def update_bid(host, port, udb, ucollection, bid_id):
    connection = MongoClient(host, port)
    db = connection[udb]
    collection = db[ucollection]

    field_list = ["AuctionTitle", "Fund", "AuctionFeeTotal"]

    # Check if bid exists before trying to update it.
    check_bid = find_bid(host, port, udb, ucollection, int(bid_id))
    if not check_bid:
        return None

    for field in field_list:
        # Loop here is for input validation.
        loop_condition = True
        while loop_condition:
            user_choice = input("Update {0}... (Y/N) ".format(field))
            if user_choice is 'Y' or user_choice is 'y':
                try:
                    user_value = input("Enter a new value: ")
                    update = {field: user_value}
                    collection.update_one({"AuctionID": bid_id}, {"$set": update})
                except:
                    abort(400, str("Error in process. Aborted."))

                loop_condition = False
            elif user_choice is 'N' or user_choice is 'n':
                loop_condition = False
            else:
                print("Invalid input, please enter valid input")

def delete_bid(host, port, udb, ucollection, bid_id):
    connection = MongoClient(host, port)
    db = connection[udb]
    collection = db[ucollection]

    try:
        collection.delete_one({'AuctionID': bid_id})
        print("Bid successfully removed from the database")
    except:
        abort(400, str("Error in process. Aborted."))


import pygame
from Player import Player
from Monster import Mummy, Alien
from CometEvent import CometFallEvent
from Sounds import  SoundManager

class Game:
    def __init__(self):
        self.isPlaying = False
        #self.isPlaying = True
        self.allPlayers = pygame.sprite.Group()
        self.player = Player(self)
        self.allPlayers.add(self.player)
        self.pressed = {}
        self.allMonsters = pygame.sprite.Group()
        self.cometEvent = CometFallEvent(self)
        self.score = 0
        self.font = pygame.font.SysFont("monospace", 16)
        self.soundManager = SoundManager()

    def addScore(self, points=10):
        self.score += points
    def start(self):
        self.isPlaying = True
        self.spawnMonster(Mummy)
        self.spawnMonster(Mummy)
        self.spawnMonster(Alien)
    def gameOver(self):
        self.allMonsters = pygame.sprite.Group()
        self.cometEvent.allComets = pygame.sprite.Group()
        self.cometEvent.resetPercent()
        self.player.health = self.player.maxHealth
        self.isPlaying = False
        self.score = 0
        self.soundManager.play("gameOver")
    def update(self,screen):
        #score

        #font = pygame.font.Font("/asste/monospace", 16)
        scoreText = self.font.render(f"Score : {self.score}",1,(0,0,0))
        screen.blit(scoreText,(20,20))
        screen.blit(self.player.image, self.player.rect)
        self.player.updateHeathBar(screen)
        self.player.updateAnimation()
        self.cometEvent.updateBar(screen)
        for projectile in self.player.allProjectiles:
            projectile.move()
        for monster in self.allMonsters:
            monster.forward()
            monster.updateAnimation()
            monster.updateHeathBar(screen)
        for comet in self.cometEvent.allComets:
            comet.fall()
        self.allMonsters.draw(screen)
        self.player.allProjectiles.draw(screen)
        self.cometEvent.allComets.draw(screen)
        if self.pressed.get(pygame.K_RIGHT) and self.player.rect.x + self.player.rect.width < screen.get_width():
            self.player.move("right")
        if self.pressed.get(pygame.K_LEFT) and self.player.rect.x > 0:
            self.player.move("left")
    def spawnMonster(self,monsterClassName):
        self.allMonsters.add(monsterClassName.__call__(self))

    def checkCollison(self, sprite, group):

        return pygame.sprite.spritecollide(sprite, group, False, pygame.sprite.collide_mask)


from sklearn.svm import SVC
import pandas as pd
import numpy as np
import gc, pickle, time
import openpyxl
from skopt import gp_minimize, dummy_minimize
from skopt.space import Real, Integer
from skopt.utils import use_named_args
from skopt.plots import plot_convergence
from sklearn.metrics import matthews_corrcoef, mean_squared_error
# Own Scripts
# from own_package.svr import  run_svr
from own_package.others import print_array_to_excel, create_excel_file, print_df_to_excel

class SVMmodel:
    def __init__(self, fl, gamma=1):
        # Model setup
        self.features_dim = fl.features_dim
        self.labels_dim = fl.labels_dim  # Assuming that each task has only 1 dimensional output
        self.model = SVC(kernel='rbf', gamma=gamma, probability=True)  # Set up SVM radial basis model

    def train_model(self, fl):
        training_features = fl.features
        training_labels = fl.labels
        self.model.fit(training_features, training_labels)
        return self

    def predict(self, eval_fl):
        features = eval_fl.features
        y_pred = self.model.predict(features)
        return y_pred

def run_classification(grid_fl_dir, write_dir, gamma):
    # Load grid fl
    with open(grid_fl_dir, 'rb') as handle:
        fl = pickle.load(handle)
    # Create 10 fold for cross validation
    fl_store = fl.create_kf(k_folds=10, shuffle=True)
    # Run k model instance to perform skf
    # Results dataframe has the columns: ['idx', 'fold', 'CNT', 'PVA', 'Label', 'Prediction']
    # For each fold, append the fold information to the following lists:
    val_idx = []
    folds = []
    val_features = []
    val_labels = []
    predicted_labels_store = []
    # fl_store is a 10 item list where each item is a tuple containing the train and val fl
    for fold, fl_tuple in enumerate(fl_store):
        instance_start = time.time()
        (ss_fl, i_ss_fl) = fl_tuple  # ss_fl is training fl, i_ss_fl is validation fl
        # Train model
        model = SVMmodel(fl=ss_fl, gamma=gamma)
        model.train_model(fl=ss_fl)
        # Evaluation
        predicted_labels = model.predict(i_ss_fl)
        # Saving model
        save_model_name = write_dir + '/models/svm_' + str(fold + 1) + '.pkl'
        print('Saving instance {} model in {}'.format(fold + 1, save_model_name))
        with open(save_model_name, 'wb') as handle:
            pickle.dump(model, handle, protocol=pickle.HIGHEST_PROTOCOL)
        # Preparing data to put into new_df that consists of all the validation dataset and its predicted labels
        val_idx.extend(i_ss_fl.idx)
        folds.extend([fold] * i_ss_fl.count)  # Make a col that contains the fold number for each example
        if len(val_features):
            val_features = np.concatenate((val_features, i_ss_fl.features), axis=0)
        else:
            val_features = i_ss_fl.features
        val_labels.extend(i_ss_fl.labels)
        predicted_labels_store.extend(predicted_labels)
        # Printing one instance summary.
        instance_end = time.time()
        print(
            '\nFor k-fold run {} out of {}. Each fold has {} examples. Time taken for '
            'instance = {}\n'
            '####################################################################################################'
                .format(fold + 1, 10, i_ss_fl.count, instance_end - instance_start))

    # Calculating metrics based on complete validation prediction
    mcc = matthews_corrcoef(y_true=val_labels, y_pred=predicted_labels_store)

    # Creating dataframe to print into excel later.
    results_df = np.concatenate((np.array(folds)[:, None],  # Convert 1d list to col. vector
                             val_features,
                             np.array(val_labels)[:, None],
                             np.array(predicted_labels_store)[:, None]) , axis=1)
    headers = ['folds'] + \
              ['CNT', 'PVA'] + \
              ['Labels'] + \
              ['Prediction']
    # val_idx is the original position of the example in the data_loader
    results_df = pd.DataFrame(data=results_df, columns=headers, index=val_idx)
    # Create excel file and print results to excel
    excel_file = create_excel_file(f'{write_dir}/classifier_results.xlsx')
    print('Writing into' + excel_file)
    wb = openpyxl.Workbook()
    # Create results sheet
    wb.create_sheet('results')
    ws = wb['results']
    # Print results df
    print_df_to_excel(df=results_df, ws=ws)
    # Writing hyperparameter information at the side
    start_col = len(results_df.columns) + 3
    headers = ['mcc', 'gamma']
    values = [mcc, gamma]
    print_array_to_excel(np.array(headers), (1, start_col + 1), ws, axis=1)
    print_array_to_excel(np.array(values), (2, start_col + 1), ws, axis=1)
    wb.save(excel_file)
    wb.close()

def svm_hparam_opt(grid_fl_dir, total_run, write_excel_dir):
    with open(grid_fl_dir, 'rb') as fp:
        fl = pickle.load(fp)

    run_count = 0
    gamma = Real(low=0.1, high=300, name='gamma')
    dimensions = [gamma]
    default_parameters = [130]

    fl_store = fl.create_kf(k_folds=10, shuffle=True)

    @use_named_args(dimensions=dimensions)
    def fitness(gamma):
        nonlocal run_count, fl_store
        run_count += 1
        # Run k model instance to perform skf
        predicted_labels_store = []
        val_labels = []
        for fold, fl_tuple in enumerate(fl_store):
            (ss_fl, i_ss_fl) = fl_tuple  # ss_fl is training fl, i_ss_fl is validation fl

            # Training
            model = SVMmodel(fl=ss_fl, gamma=gamma)
            model.train_model(fl=ss_fl)

            # Evaluation
            predicted_labels = model.predict(i_ss_fl).flatten().tolist()
            predicted_labels_store.extend(predicted_labels)
            val_labels.extend(i_ss_fl.labels.flatten().tolist())

        # Calculating metrics based on complete validation prediction
        mcc = matthews_corrcoef(y_true=val_labels, y_pred=predicted_labels_store)
        if run_count % 10 == 0:  # Print every 10 iteration
            print(f'Run Number {run_count}')
        return -mcc

    search_result = gp_minimize(func=fitness,
                                dimensions=dimensions,
                                acq_func='EI',  # Expected Improvement.
                                n_calls=total_run,
                                x0=default_parameters)
    print('Best Loss = {}'.format(search_result.fun))
    print('Best Gamma = {}'.format(search_result.x[0]))
    x = [x[0] for x in search_result.x_iters]
    results = pd.DataFrame([x] + [(-search_result.func_vals).tolist()]).T
    results.columns = ['Gamma', 'mcc']
    results = results.sort_values(by='mcc', ascending=False)

    write_excel_dir = create_excel_file(write_excel_dir)
    wb = openpyxl.load_workbook(write_excel_dir)
    ws = wb[wb.sheetnames[-1]]
    print_df_to_excel(df=results, ws=ws)
    wb.save(write_excel_dir)
    wb.close()


import numpy as np
import gurobipy as gp
from gurobipy import GRB
from map import get_trip_time_matrix

class Controller(object):
    def __init__(self, num_stations: int, soc: int, horizon):
        self.n = num_stations
        self.horizon = horizon
        self.soc = soc
        # Type: np.array, trip time between every two stations
        self.tau = get_trip_time_matrix()
        self.tau_max = np.amax(self.tau, axis=1)
        # Charging speed relative to tau
        self.tau_c = 4
        # Depleting speed relative to tau, per 8 min consumes 2 kWh
        decharge = 1
        self.delta_b = self.tau * decharge
        # Tuning weights
        self.Vd = 1000
        self.Vr = 5
        self.Vx = 5
        self.Vp = 5
        self.Vc = 1

    def compute_actions(self, veh_idle, veh_charging, veh_occupied, reqs: np.array):
        # Number of requests from i to j in the horizon of the controller
        requests = np.copy(reqs)

        # Build a list of tuple oddtb (origin, destination, destination, time, SOC),
        # a list of tuple odtb (origin, destination, time, SOC) for rebalancing
        # a list of tuple otb (origin, time, SOC) for charging vehicles
        # and a list of tuple odt for request counting
        list_ijtb = []
        list_itb = []
        list_ijt = []
        list_ib = []
        for i in range(self.n):
            for j in range(self.n):
                for t in range(self.horizon + 1):
                    odt = i,j,t
                    list_ijt.append(odt)
                    for b in range(self.soc):
                        odtb = i,j,t,b
                        list_ijtb.append(odtb)

        for i in range(self.n):
            for b in range(self.soc):
                ob = i,b
                list_ib.append(ob)
                for t in range(self.horizon + 1):
                    otb = i,t,b
                    list_itb.append(otb)

        itb = gp.tuplelist(list_itb)
        ijt = gp.tuplelist(list_ijt)
        ijtb = gp.tuplelist(list_ijtb)

        mpc = gp.Model(name = 'AMoD_EV MILP')

        # objective function
        obj = 0
        # Control variables
        r = mpc.addVars(ijtb, vtype = GRB.INTEGER, name = 'r')
        c = mpc.addVars(itb, vtype = GRB.INTEGER, name = 'c')
        x_zo = mpc.addVars(ijtb, vtype = GRB.INTEGER, name = 'x_zo')
        d = mpc.addVars(ijt, vtype = GRB.INTEGER, lb = 0, name = 'd')

        mpc.addConstrs((x_zo[i, i, t, b] == 0 for i, t, b in itb), name = 'disallowed')

        for t in range(0, self.horizon + 1):
            for i in range(self.n):
                if t >= self.tau_max[i]:
                    available_j_0 = [j for j in range(self.n)]
                else:
                    available_j_0 = np.where((t - self.tau[i, :]) >= 0)[0].tolist()
                # for b = 0
                mpc.addConstrs((r[i, j, t, 0] == 0 for j in range(self.n)), name = 'disallowed')
                mpc.addConstrs((x_zo[i, j, t, 0] == 0 for j in range(self.n)), name = 'disallowed')
                if t == 0:
                    mpc.addConstr(veh_idle[i, 0, 0] + veh_charging[i, 0] == r[i, i, 0, 0] + c[i, 0, 0], name = 'Initial vaccant veh conservation')
                if t > 0:
                    mpc.addConstr(veh_idle[i, t, 0] == r[i, i, t, 0]+ c[i, t, 0], name = 'Initial vaccant veh conservation')

                for b in range(1, self.soc):
                    available_j = [j for j in available_j_0 if (b + self.delta_b[i, j]) < self.soc]
                    reachable_j = [j for j in range(self.n) if (b - self.delta_b[i, j]) > 0]
                    unreachable_j = [j for j in range(self.n) if j not in reachable_j]

                    mpc.addConstrs((r[i, j, t, b] == 0 for j in unreachable_j), name = 'disallowed')
                    mpc.addConstrs((x_zo[i, j, t, b] == 0 for j in unreachable_j), name = 'disallowed')

                    left_sum0 = 0
                    right_sum0 = 0
                    for j in reachable_j:
                        right_sum0 += r[i, j, t, b] + x_zo[i, j, t, b]
                    if t == 0:
                        mpc.addConstr(veh_idle[i, 0, b] + veh_charging[i, b] == right_sum0 + c[i, 0, b], name = 'Initial vaccant veh conservation')

                    # Constraints during the horizon
                    else:
                        for j in available_j:
                            left_sum0 += r[j, i, t-self.tau[i, j], b+self.delta_b[i, j]] + x_zo[j, i, t-self.tau[i, j], b+self.delta_b[i, j]]
                        c_sum = 0
                        if b == self.soc - 1:
                            for k in range(b):
                                if k + self.tau_c >= b:
                                    c_sum += c[i, t - 1, k]
                        if self.tau_c <= b < self.soc - 1:
                            c_sum = c[i, t - 1, b - self.tau_c]
                        mpc.addConstr(veh_idle[i, t, b] + c_sum + left_sum0 == c[i, t, b] + right_sum0, name = 'vaccant veh conservation')

                for j in range(self.n):
                    if t == 0:
                        mpc.addConstr(d[i, j, 0] == requests[i, j, 0] - x_zo.sum(i, j, 0, '*'), name = 'Remain requests t=0')
                        obj -= self.Vd * d[i, j, 0] + self.tau[i, j] * (self.Vr * r.sum(i, j, 0, '*') + self.Vx * x_zo.sum(i, j, 0, '*'))
                    else:
                        mpc.addConstr(d[i, j, t] == requests[i, j, t] + d[i, j ,t-1] - x_zo.sum(i, j, t, '*'), name = 'Remain requests')
                        obj -= self.Vd * d[i, j, t] + self.tau[i, j] * (self.Vr * r.sum(i, j, t, '*') + self.Vx * x_zo.sum(i, j, t, '*'))
                obj -= self.Vc * c.sum(i, t, '*')

        mpc.setObjective(obj, GRB.MAXIMIZE)
        mpc.Params.MIPFocus = 3
        mpc.Params.MIPGap = 0.01
        # Verify model formulation and save as MPS file
        mpc.write('AMoD_EV MILP.lp')
        # Run optimization engine
        mpc.optimize()

        # Output control policy
        x_zo_0 = np.zeros((self.n, self.n, self.soc))
        x_so_0 = np.zeros((self.n, self.n, self.n, self.soc))
        p_zo_0 = np.zeros((self.n, self.n, self.n, self.soc))
        p_so_0 = np.zeros((self.n, self.n, self.n, self.soc))
        r_0 = np.zeros((self.n, self.n, self.soc))
        c_0 = np.zeros((self.n, self.soc))

        for i in range(self.n):
            for b in range(self.soc):
                c_0[i, b] = c[i, 0, b].x
                for j in range(self.n):
                    r_0[i, j, b] = r[i, j, 0, b].x
                    x_zo_0[i, j, b] = x_zo[i, j, 0, b].x

        print('c_0 = ', np.where(c_0 != 0))
        print('r_0 = ', np.where(r_0 != 0))
        print('x_zo_0 = ', np.where(x_zo_0 != 0))

        return x_zo_0, x_so_0,p_zo_0, p_so_0, r_0, c_0


import datetime
import logging
import random
import re

logger = logging.getLogger(__name__)

class Generator:

    last_student_id = 0

    grades = [
        # Elementary school grades | range (0, 6)
        'Pre-Kindergarten', 'Kindergarten', '1st', '2nd', '3rd', '4th',

        # Middle school grades | range (6, 10)
        '5th', '6th', '7th', '8th',

        # High school grades | range (10, 14)
        '9th', '10th', '11th', '12th'
    ]

    is_lep = ['Yes', 'No']

    designation = ['None', 'Special Ed', '504']

    ethnicity = [
        'Hispanic or Latino', 'Hispanic', 'Latino',
        'American Indian', 'Indian', 'American',
        'Alaskan Native', 'Alaskan', 'Asian',
        'Native Hawaiian', 'Hawaiian', 'Black',
        'Black or African American', 'African',
        'African American', 'White', 'Caucasian',
        'White or Caucasian', 'Not Disclosed'
    ]

    sex = ['Male', 'Female']

    has_reduced_lunch = ['Yes', 'No']

    months_max_days = {
        1: 31, 2: 29, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31
    }

    grade_to_year = {
        'Pre-Kindergarten': 2015,
        'Kindergarten': 2014,
        '1st': 2013,
        '2nd': 2012,
        '3rd': 2011,
        '4th': 2010,
        '5th': 2009,
        '6th': 2008,
        '7th': 2007,
        '8th': 2006,
        '9th': 2005,
        '10th': 2004,
        '11th': 2003,
        '12th': 2002
    }

    @staticmethod
    def student_grade(school_name):
        student_grade = Generator.grades[random.randrange(0, 14)]

        if re.match(r'.*High.*', school_name):
            student_grade = Generator.grades[random.randrange(10, 14)]

        elif re.match(r'.*Middle.*', school_name):
            student_grade = Generator.grades[random.randrange(6, 10)]

        elif re.match(r'.*Elementary.*', school_name):
            student_grade = Generator.grades[random.randrange(0, 6)]

        return student_grade

    @staticmethod
    def student_date_of_birth(student_grade):
        year = Generator.grade_to_year[student_grade]
        month = random.randrange(1, 13)
        day = random.randrange(1, Generator.months_max_days[month])

        date_of_birth = datetime.datetime(year, month, day).strftime('%m/%d/%Y')
        return date_of_birth

    @staticmethod
    def randomly_select(selection_list):
        return selection_list[random.randrange(len(selection_list))]

    @staticmethod
    def student_is_lep():
        return Generator.randomly_select(Generator.is_lep)

    @staticmethod
    def student_designation():
        return Generator.randomly_select(Generator.designation)

    @staticmethod
    def student_sex():
        return Generator.randomly_select(Generator.sex)

    @staticmethod
    def student_ethnicity():
        return Generator.randomly_select(Generator.ethnicity)

    @staticmethod
    def student_has_reduced_lunch():
        return Generator.randomly_select(Generator.has_reduced_lunch)

    @staticmethod
    def student_id():
        Generator.last_student_id += 1
        return Generator.last_student_id

class Transformer:

    @staticmethod
    def get_first_name(student):
        if type(student) is str:
            first_name = student.split(' ')[0]
        else:
            first_name = student['name']['first']

        return first_name.capitalize()

    @staticmethod
    def get_last_name(student):
        if type(student) is str:
            last_name = student.split(' ')[1]
        else:
            last_name = student['name']['last']

        return last_name.capitalize()

    @staticmethod
    def create_full_name(student):
        return Transformer.get_first_name(student) + ' ' + Transformer.get_last_name(student)

    @staticmethod
    def create_student_profile(student, school):
        student_grade = Generator.student_grade(school['Name'])
        return {
            'Name': Transformer.create_full_name(student),
            'School': school['Name'],
            'Grade': student_grade,
            'Designation': Generator.student_designation(),
            'Lep': Generator.student_is_lep(),
            'Student Id': Generator.student_id(),
            'Date of Birth': Generator.student_date_of_birth(student_grade),
            'Reduced lunch fee': Generator.student_has_reduced_lunch(),
            'Race': Generator.student_ethnicity(),
            'Sex': Generator.student_sex()
        }


# AI for Self Driving Car

# Importing the libraries

import numpy as np
import random
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torch.autograd as autograd
from torch.autograd import Variable

# Creating the architecure of the Neural Network

class Network(nn.Module):

    def __init__(self, input_size, nb_action, hidden_neurons):
        super(Network, self).__init__()
        self.input_size = input_size
        self.nb_action = nb_action
        self.fc0 = nn.Linear(input_size, hidden_neurons)
        self.fc1 = nn.Linear(hidden_neurons, hidden_neurons)
        self.fc11 = nn.Linear(hidden_neurons, hidden_neurons)
        self.fc2 = nn.Linear(hidden_neurons, nb_action)

    def forward(self, state):  # Forward propagation
        x0 = F.relu(self.fc0(state))
        x1 = F.relu(self.fc1(x0))
        x11 = F.relu(self.fc11(x1))
        q_values = self.fc2(x11)
        return q_values

# Implementing Experience Replay
class ReplayMemory(object):

    def __init__(self, capacity):
        self.capacity = capacity
        self.memory = []

    def push(self, event):
        self.memory.append(event)
        if len(self.memory) > self.capacity:
            del self.memory[0]

    def sample(self, batch_size):
        samples = zip(*random.sample(self.memory, batch_size))
        return map(lambda x: Variable(torch.cat(x, 0)), samples)

# Implementaing Deep Q Learning

class Dqn:

    input_size = 0
    nb_action = 0

    driving_config = None

    def __init__(self, input_size, nb_action, config):

        self.driving_config = config

        self.gamma = self.driving_config.dqn_gamma
        self.reward_window = []
        self.input_size = input_size
        self.nb_action = nb_action

        self.model = Network(input_size, nb_action, self.driving_config.dqn_hidden_neurons)
        self.memory = ReplayMemory(100000)
        self.optimizer = optim.Adam(self.model.parameters(), lr=self.driving_config.dqn_learning_rate)
        self.last_state = torch.Tensor(input_size).unsqueeze(0)
        self.last_action = 0
        self.last_reward = 0

    def reset(self, config):

        self.driving_config = config

        self.reward_window = []
        self.model = Network(self.input_size, self.nb_action, self.driving_config.dqn_hidden_neurons)
        self.memory = ReplayMemory(100000)
        self.optimizer = optim.Adam(self.model.parameters(), lr=self.driving_config.dqn_learning_rate)
        self.last_state = torch.Tensor(self.input_size).unsqueeze(0)
        self.last_action = 0
        self.last_reward = 0

    def select(self, state):
        probs = F.softmax(self.model(Variable(state, volatile=True)) * self.driving_config.dqn_temperature)
        action = probs.multinomial(num_samples=1)
        return action.data[0, 0]

    def learn(self, batch_state, batch_next_state, batch_reward, batch_action):
        outputs = self.model(batch_state).gather(1, batch_action.unsqueeze(1)).squeeze(1)
        next_outputs = self.model(batch_next_state).detach().max(1)[0]
        target = self.gamma * next_outputs + batch_reward
        td_loss = F.smooth_l1_loss(outputs, target)
        self.optimizer.zero_grad()
        td_loss.backward(retain_variables=True)
        self.optimizer.step()

    def update(self, reward, signal):
        new_state = torch.Tensor(signal).float().unsqueeze(0)
        last_action_tensor = torch.LongTensor([int(self.last_action)])  # converting an int to a tensor
        reward_tensor = torch.Tensor([self.last_reward])
        self.memory.push((self.last_state, new_state, last_action_tensor, reward_tensor))
        action = self.select(new_state)
        if len(self.memory.memory) > self.driving_config.dqn_sample_size:
            # print("sampling")
            batch_state, batch_next_state, batch_action, batch_reward = self.memory.sample(self.driving_config.dqn_sample_size)
            # print("learning")
            self.learn(batch_state, batch_next_state, batch_reward, batch_action)

        self.last_action = action
        self.last_state = new_state
        self.last_reward = reward
        self.reward_window.append(reward)
        if len(self.reward_window) > 1000:
            del self.reward_window[0]

        # print("acting")
        return action

    def score(self):
        return sum(self.reward_window) / (len(self.reward_window) + 1)  # trick to avoid dividing by 0

    def save(self):
        torch.save({
            'state_dict': self.model.state_dict(),
            'optimizer': self.optimizer.state_dict()
        }, 'last_brain.pth')

    def load(self):
        if os.path.isfile('last_brain.pth'):
            print("Loading last save")
            checkpoint = torch.load('last_brain.pth')
            self.model.load_state_dict(checkpoint['state_dict'])
            self.optimizer.load_state_dict(checkpoint['optimizer'])
            print("Loaded")
        else:
            print("No save to load")


from collections import defaultdict
from typing import List

class Solution:
    def numPairsDivisibleBy60(self, songs: List[int]) -> int:
        remainders = defaultdict(int)
        number_of_pairs, divisor = 0, 60

        for time in songs:
            if time % divisor == 0:
                number_of_pairs += remainders[0]
            else:
                number_of_pairs += remainders[divisor-time % divisor]
            remainders[time % divisor] += 1

        return number_of_pairs


"""Currency converter"""

import codecs
import json
import numbers as num
import os
import re
import sys
from modules.check_currency import CheckCurrency
from modules.json_out import json_out
from modules.help_me import HelpMe
from modules.url_reader import URLReader

__all__=["CurrencyConverter"]

class CurrencyConverter:
    def __init__(self, argv):
        self.currency = CheckCurrency()
        if not self.currency.table_ok:
            self._exit()
        self.help_me = HelpMe()
        self.output_json = json_out()
        self._params_processing(argv)

    # Params processing(what params, encode, check).
    def _params_processing(self, argv):
        if argv and len(argv) in range(5,8):
            _amount = -1
            _input_c = []
            _output_c = []
            for argv_item in range(0, len(argv)):
                if argv[argv_item] == "--amount":
                    _amount = argv[argv_item+1]
                elif argv[argv_item] == "--input_currency":
                    # [0] - original, [1] - encoded
                    _input_c.append(argv[argv_item+1])
                    _input_c.append(argv[argv_item+1].encode('UTF-8'))
                elif argv[argv_item] == "--output_currency":
                    if (argv_item+1) < len(argv):
                        # [0] - original, [1] - encoded
                        _output_c.append(argv[argv_item+1])
                        _output_c.append(argv[argv_item+1].encode('UTF-8'))
            # Processed params -> show them.
            p_amout, p_input, p_output = self._processed_params(_amount,
                                                                _input_c,
                                                                _output_c)
            # Converts amount.
            self._convert(p_amout, p_input, p_output)
            # Shows result.
            self._show()
        else:
            for argv_item in range(0, len(argv)):
                if argv[argv_item] == "--supported_currency":
                    print (self.help_me.supported_currency)
                    self._exit()

    # Check each valid param.
    def _processed_params(self, amount, input_c, output_c):
        _v_amount = -1
        _v_input_c = None
        _v_output_c = []
        finished_ok = False
        # Checks if amount is usable number.
        if str.isdigit(amount) or self._is_float(amount):
            _v_amount = float(amount)
        else:
            print (""">>>Wrong param 'amount', its not processable.\n""")
            self._help_tail()
        # Gets currency codes involved in the conversion.
        _v_input_c, _v_output_c = self.currency._currency_codes(input_c,
                                                                output_c)
        if _v_amount != -1 and _v_input_c and _v_output_c:
            finished_ok = True
        else:
            print ('>>>Wrong params, check them.\n')
            self._help_tail()
        return _v_amount, _v_input_c, _v_output_c

    # Converts the amount to output currency.
    def _convert(self, amount, c_in, c_out):
        url_reader = URLReader()
        # Sets the amount, input currency code.
        self.output_json['input']['amount'] = amount
        self.output_json['input']['currency'] = c_in
        # Sets output currency + converted amount.
        for item in c_out:
            ex_site = "http://www.x-rates.com/calculator/"
            url_l = "%s?from=%s&to=%s&amount=%.2f" % (ex_site, c_in, item,
                                                      amount)
            value_out = url_reader.get_url_response(url_l)
            if value_out['status'] == 200 and value_out['body']:
                new_amount = self._get_new_amount(value_out['body'])
                if  new_amount:
                    self.output_json['output'][item] = new_amount

    # Parses a new amount from a url response.
    def _get_new_amount(self, raw_text):
        ret_amount = None
        founded = re.findall(r'<span class="ccOutputRslt">(.*?)<span', raw_text)
        if founded:
            ret_amount = founded[0]
        return ret_amount

    # Shows the output.
    def _show(self):
        _json_out = json.dumps(self.output_json)
        print (_json_out)

    # Help globall tail.
    def _help_tail(self):
        print (self.help_me.how_to)
        self._exit()

    # Its float number?
    def _is_float(self, input_str):
        ret_status = True
        try:
            float(input_str)
        except:
            ret_status = False
        return ret_status

    def _exit(self):
        sys.exit()

if __name__=='__main__':
    converter = CurrencyConverter(sys.argv)


import numpy as np
import matplotlib.pyplot as plt
import scipy.io
import math
import sklearn
import sklearn.datasets

from opt_utils import load_params_and_grads, initialize_parameters, forward_propagation, backward_propagation
from opt_utils import compute_cost, predict, predict_dec, plot_decision_boundary, load_dataset
from testCase import *

def update_parameters_with_gd(parameters, grads, learning_rate):
    '''
    使用梯度下降算法更新参数
    :param parameters:包含需要更新的参数的python字典
                    parameters['W' + str(l)] = Wl
                    parameters['b' + str(l)] = bl
    :param grads:python字典，用以更新参数的梯度值
                    grads['dW' + str(l)] = dWl
                    grads['db' + str(l)] = dbl
    :param learning_rate:学习率
    :return:
        :parameters：更新之后的参数
    '''
    L = len(parameters) // 2
    for l in range(L):  # range(L):0 -> L-1
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
    return parameters

def random_mini_batches(X, Y, mini_batch_size=64, seed=0):
    '''
    从（X,Y）中创建一个随机的minibatches的列表。
    :param X:输入数据
    :param Y:真实标签值，1用蓝点、0用红点表示。形状是（1，样本数）
    :param mini_batch_size:minibatches的大小
    :param seed:随机种子
    :return:
        minibatches:一个同步列表（mini_batch_X,minibatch_Y.
    '''

    np.random.seed(seed)
    m = X.shape[1]
    mini_batches = []
    # 第一步：打乱顺序
    permutation = list(np.random.permutation(m))  # 它会返回一个长度为m的随机数组，且里面的数是0到m-1
    shuffled_X = X[:, permutation]  # 将每一列的数据按permutation的顺序来重新排列。
    shuffled_Y = Y[:, permutation].reshape((1, m))

    # 第二步：分割
    num_complete_minibatches = math.floor(m / mini_batch_size)  # 把你的训练集分割成多少份,请注意，如果值是99.99，那么返回值是99，剩下的0.99会被舍弃
    for k in range(0, num_complete_minibatches):
        mini_batch_X = shuffled_X[:, k * mini_batch_size:(k + 1) * mini_batch_size]
        mini_batch_Y = shuffled_Y[:, k * mini_batch_size:(k + 1) * mini_batch_size]
        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)
    # 如果训练集的大小刚好是mini_batch_size的整数倍，那么这里已经处理完了
    # 如果训练集的大小不是mini_batch_size的整数倍，那么最后肯定会剩下一些，我们要把它处理了
    if m % mini_batch_size != 0:
        mini_batch_X = shuffled_X[:, mini_batch_size * num_complete_minibatches:]
        mini_batch_Y = shuffled_Y[:, mini_batch_size * num_complete_minibatches:]
        mini_batch = (mini_batch_X, mini_batch_Y)  # 列表里包含着一个元组
        mini_batches.append(mini_batch)
    """
       #博主注：
       #如果你不好理解的话请看一下下面的伪代码，看看X和Y是如何根据permutation来打乱顺序的。
       x = np.array([[1,2,3,4,5,6,7,8,9],
                     [9,8,7,6,5,4,3,2,1]])
       y = np.array([[1,0,1,0,1,0,1,0,1]])
       random_mini_batches(x,y)
       permutation= [7, 2, 1, 4, 8, 6, 3, 0, 5]
       shuffled_X= [[8 3 2 5 9 7 4 1 6]
                    [2 7 8 5 1 3 6 9 4]]
       shuffled_Y= [[0 1 0 1 1 1 0 1 0]]
    """
    return mini_batches

def initialize_velocity(parameters):
    """
    初始化速度，velocity是一个字典：
        - keys: "dW1", "db1", ..., "dWL", "dbL"
        - values:与相应的梯度/参数维度相同的值为零的矩阵。
    :param parameters:一个字典，包含了以下参数：
            parameters["W" + str(l)] = Wl
            parameters["b" + str(l)] = bl
    返回:
        v - 一个字典变量，包含了以下参数：
            v["dW" + str(l)] = dWl的速度
            v["db" + str(l)] = dbl的速度
    """
    L = len(parameters) // 2
    v = {}
    for l in range(L):
        v["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
        v["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])
    return v

def update_parameters_with_momentum(parameters, grads, v, beta, learning_rate):
    """
    使用动量更新参数
    :param parameters:一个字典类型的变量，包含了以下字段：
            parameters["W" + str(l)] = Wl
            parameters["b" + str(l)] = bl
    :param grads:个包含梯度值的字典变量，具有以下字段：
            grads["dW" + str(l)] = dWl
            grads["db" + str(l)] = dbl
    :param v:包含当前速度的字典变量，具有以下字段：
            v["dW" + str(l)] = ...
            v["db" + str(l)] = ...
    :param beta:超参数，动量，实数
    :param learning_rate:学习率，实数
    :return:
         parameters 更新后的参数字典
         v - 包含了更新后的速度变量
    """
    L = len(parameters) // 2

    for l in range(L):
        # 计算速度(指数平均加权)
        v["dW" + str(l + 1)] = beta * v["dW" + str(l + 1)] + (1 - beta) * grads["dW" + str(l + 1)]
        v["db" + str(l + 1)] = beta * v["db" + str(l + 1)] + (1 - beta) * grads["db" + str(l + 1)]
        # 更新参数
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * v["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * v["db" + str(l + 1)]

    return parameters, v

def initialize_adam(parameters):
    """
    将V和s都初始化为python字典
        - keys: "dW1", "db1", ..., "dWL", "dbL"
        - values：与对应的梯度/参数相同维度的值为零的numpy矩阵
    :param parameters:包含了以下参数的字典变量：
            parameters["W" + str(l)] = Wl
            parameters["b" + str(l)] = bl
    :return:
        v - 包含梯度的指数加权平均值，字段如下：
            v["dW" + str(l)] = ...
            v["db" + str(l)] = ...
        s - 包含平方梯度的指数加权平均值，字段如下：
            s["dW" + str(l)] = ...
            s["db" + str(l)] = ...
    """
    L = len(parameters) // 2
    v = {}
    s = {}
    # 初始化v,s.输入：parameters.输出：v,s.
    for l in range(L):
        v["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
        v["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])
        s["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
        s["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])

    return v, s

def update_parameters_with_adam(parameters, grads, v, s, t, learning_rate=0.01, beta1=0.9, beta2=0.999, epsilon=1e-8):
    """
 使用Adam更新参数,，因为传输的字典，所以形参修改，实参也会被修改。
    :param parameters: 包含了以下字段的字典：
            parameters['W' + str(l)] = Wl
            parameters['b' + str(l)] = bl
    :param grads: 包含了梯度值的字典，有以下key值：
            grads['dW' + str(l)] = dWl
            grads['db' + str(l)] = dbl
    :param v: Adam的变量，是一个字典类型的变量,包含梯度的指数加权平均值，字段如下：
            v["dW" + str(l)] = ...
            v["db" + str(l)] = ...
    :param s: Adam的变量，是一个字典类型的变量,包含平方梯度的指数加权平均值，字段如下：
            s["dW" + str(l)] = ...
            s["db" + str(l)] = ...
    :param t: 当前迭代的次数
    :param learning_rate: 学习率
    :param beta1: 动量，超参数,用于第一阶段，使得曲线的Y值不从0开始（参见天气数据的那个图）
    :param beta2: RMSprop的一个参数，超参数
    :param epsilon: 防止除零操作（分母为0）
    :return:
        parameters - 更新后的参数
        v - 第一个梯度的移动平均值，是一个字典类型的变量
        s - 平方梯度的移动平均值，是一个字典类型的变量
    """
    L = len(parameters) // 2
    v_corrected = {}  # 偏差修正后的值
    s_corrected = {}  # 偏差修正后的值
    for l in range(L):
        # 计算momentum指数加权平均
        v["dW" + str(l + 1)] = beta1 * v["dW" + str(l + 1)] + (1 - beta1) * grads["dW" + str(l + 1)]
        v["db" + str(l + 1)] = beta1 * v["db" + str(l + 1)] + (1 - beta1) * grads["db" + str(l + 1)]
        # 用RMsprop(均方根传播)计算平方梯度的移动平均值，输入："s, grads , beta2"，输出："s"
        s["dW" + str(l + 1)] = beta2 * s["dW" + str(l + 1)] + (1 - beta2) * np.power(grads["dW" + str(l + 1)], 2)
        s["db" + str(l + 1)] = beta2 * s["db" + str(l + 1)] + (1 - beta2) * np.power(grads["db" + str(l + 1)], 2)
        # 修正初期的偏差
        v_corrected["dW" + str(l + 1)] = v["dW" + str(l + 1)] / (1 - np.power(beta1, t))
        v_corrected["db" + str(l + 1)] = v["db" + str(l + 1)] / (1 - np.power(beta1, t))
        s_corrected["dW" + str(l + 1)] = s["dW" + str(l + 1)] / (1 - np.power(beta1, t))
        s_corrected["db" + str(l + 1)] = s["db" + str(l + 1)] / (1 - np.power(beta1, t))
        # 更新参数：输入: "parameters, learning_rate, v_corrected, s_corrected, epsilon". 输出: "parameters".
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * (
                v_corrected["dW" + str(l + 1)] / (np.sqrt(s_corrected["dW" + str(l + 1)]) + epsilon))
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * (
                v_corrected["db" + str(l + 1)] / (np.sqrt(s_corrected["db" + str(l + 1)]) + epsilon))
    return parameters, v, s

def model(X, Y, layers_dims, optimizer, learning_rate=0.0007, mini_batch_size=64, beta=0.9, beta1=0.9, beta2=0.999,
          epsilon=1e-8,
          num_epochs=10000, print_cost=True, is_plot=True):
    """
    可以运行在不同优化器模式下的3层神经网络模型。
    :param X:输入数据，维度为（2，输入的数据集里面样本数量）
    :param Y:与X对应的标签
    :param layers_dims:包含层数和节点数量的列表
    :param optimizer:字符串类型的参数，用于选择优化类型，【 "gd" | "momentum" | "adam" 】
    :param learning_rate:学习率
    mini_batch_size:每个小批量数据集的大小
    :param beta:用于动量优化的一个超参数
    :param beta1:用于计算平方梯度后的指数衰减的估计的超参数
    :param beta2:用于计算梯度后的指数衰减的估计的超参数
    :param epsilon:用于在Adam中避免除零操作的超参数，一般不更改
    :param num_epochs:整个训练集的遍历次数，（视频2.9学习率衰减，1分55秒处，视频中称作“代”）,相当于之前的num_iteration
    :param print_cost:是否打印误差值，每遍历1000次数据集打印一次，但是每100次记录一个误差值，又称每1000代打印一次
    :param is_plot:是否绘制出曲线图
    :return:
            parameters:包含了学习后的参数
    """
    L = len(layers_dims)
    costs = []
    t = 0  # 每学习完一个minibatche就增加1
    seed = 10  # 随机种子
    parameters = initialize_parameters(layers_dims)  # 初始化参数
    # 选择优化器
    if optimizer == "gd":
        pass  # 不使用任何优化器，直接使用梯度下降算法
    elif optimizer == "momentum":
        v = initialize_velocity(parameters)  # 使用动量优化
    elif optimizer == "adam":
        v, s = initialize_adam(parameters)  # 使用Adam优化
    else:
        print("optimizer参数错误")
        exit(1)
    # 开始学习
    for i in range(num_epochs):
        # 定义随机minibatches,我们每次遍历数据集之后增加种子以重新排列数据集，是每次数据的顺序不同
        seed = seed + 1
        minibatches = random_mini_batches(X, Y, mini_batch_size, seed)
        for minibatch in minibatches:
            # 选择一个minibatch
            (minibatch_X, minibatch_Y) = minibatch
            # 前向传播
            A3, cache = forward_propagation(minibatch_X, parameters)
            # 计算误差
            cost = compute_cost(A3, minibatch_Y)
            # 反向传播
            grads = backward_propagation(minibatch_X, minibatch_Y, cache)
            # 更新参数
            if optimizer == "gd":
                parameters = update_parameters_with_gd(parameters, grads, learning_rate)
            elif optimizer == "momentum":
                parameters, v = update_parameters_with_momentum(parameters, grads, v, beta, learning_rate)
            elif optimizer == "adam":
                t = t + 1  # Adam counter
                parameters, v, s = update_parameters_with_adam(parameters, grads, v, s,
                                                               t, learning_rate, beta1, beta2, epsilon)

        # 记录误差值
        if i % 100 == 0:
            costs.append(cost)
            # 是否打印误差值
            if print_cost and i % 1000 == 0:
                print("第" + str(i) + "次遍历整个数据集，当前误差值：" + str(cost))

    # 是否绘制曲线图
    if is_plot:
        plt.plot(costs)
        plt.show()
        plt.ylabel('cost')
        plt.xlabel('epochs (per 100)')
        plt.title("Learning rate = " + str(learning_rate))
        plt.show()

    return parameters

train_X, train_Y = load_dataset()
layers_dims = [train_X.shape[0], 5, 2, 1]

# # 1、使用普通的梯度下降算法
# parameters = model(train_X, train_Y, layers_dims, optimizer="gd")

# # 2、使用动量的梯度下降算法
# parameters = model(train_X, train_Y, layers_dims, beta=0.9, optimizer="momentum")

# 3、使用adam优化的梯度下降算法
parameters = model(train_X, train_Y, layers_dims, optimizer="adam")

# 进行预测
predictions = predict(train_X, train_Y, parameters)
# 绘制分类图
plt.rcParams['figure.figsize'] = (7.0, 4.0)  # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

plt.title("Model with Gradient Descent optimization")
axes = plt.gca()
axes.set_xlim([-1.5, 2.5])
axes.set_ylim([-1, 1.5])
plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


import matplotlib.pyplot as plt
import numpy as np
import sys

filename = sys.argv[1]
#filename = "a2q_12_leaves#_summary.txt"
file = open(filename,'r')
raw_data = [line.split() for line in file.readlines()]
data = np.array(raw_data[:])[:,-1]
temp = data.astype(np.float)
fig = plt.figure()
#max_val=float(data[np.argmax(data)])
#print(max_val)
#bin=np.linspace(0,max_val+1,100)
#plt.hist(temp, bins='sqrt')
#plt.show()

plt.plot(np.arange(len(raw_data)), temp)
fig.savefig(filename + '.png')


from collections import OrderedDict

def check_line(index, board, map):
    i = 0
    for j in range(len(board)):
        i = max(i, map.get(board[index][j]))
    return i

def check_col(index, board, map):
    i = 0
    for j in range(len(board)):
        i = max(i, map.get(board[j][index]))
    return i

def compute_board(board, draw_map):
    indexes = []
    for i in range(len(board)):
        indexes.append(check_line(i, board, draw_map))

    for i in range(len(board)):
        indexes.append(check_col(i, board, draw_map))

    return (min(indexes), bool(len(indexes) // 5), len(indexes) % 5)

def compute_score(b_index, tup):
    global boards
    global draw_set
    global draw
    last_index, isCol, index = tup
    board = boards[b_index]
    s = 0
    for i in range(len(board)):
        for j in range(len(board)):
            if draw_set.get(board[i][j]) <= last_index:
                pass
            else:
                s += board[i][j]
    return s * draw[last_index]

with open("/home/vaascoo/Documents/Programming/rust/AOC21/4/input.txt", "r") as f:
    draw = list(int(el) for el in f.readline().strip().split(','))
    f.readline()
    raw = list(map(lambda s : list(int(el) for el in s.replace('  ', ' ').strip().split(' ')), list(filter(lambda s : s != '\n', f.readlines()))))

boards = []
for i in range(5, len(raw) + 5, 5):
    boards.append(raw[i - 5 : i])
draw_set = OrderedDict()
for i in range(len(draw)):
    draw_set[draw[i]] = i

winner = float('inf')
winner_index = None
winner_tuple = None

last = -1
last_index = None
last_tuple = None

i = 0
for board in boards:
    tup = compute_board(board, draw_set)
    if (tup[0] < winner):
        winner = tup[0]
        winner_index = i
        winner_tuple = tup
    if (tup[0] > last):
        last = tup[0]
        last_index = i
        last_tuple = tup
    i += 1

print(compute_score(winner_index, winner_tuple))
print(compute_score(last_index, last_tuple))


import pyglet
from pyglet.window import key
from pyglet.window import mouse
import characters
import render
import config
import world
from rect import Rect
import debug

class MainWindow(pyglet.window.Window):
    def __init__(self):
        display = pyglet.canvas.get_display()
        screen = display.get_default_screen()
        width = 1000
        height = 256
        x = (screen.width - width) / 2
        y = (screen.height - height) / 4
        super(MainWindow, self).__init__(width=width, height=height)
        self.set_location(x, y)

window = MainWindow()

def draw(dt):
    window.clear()
    render.draw()
    window.flip()

@window.event
def on_key_press(symbol, modifiers):
    if symbol == key.ESCAPE:
        exit()
    if symbol == key.ENTER and (modifiers & key.MOD_SHIFT):
        window.set_fullscreen(not window.fullscreen)
        world.set_rect(Rect(0,0,window.width,window.height))

@window.event
def on_mouse_press(x, y, button, modifiers):
    debug.handle_mouse_press(x, y, button, modifiers)

def main():
    render.init()
    world.init()

    render.register_background(config.BG1)

    pyglet.app.event_loop.idle = idle
    pyglet.clock.schedule_interval(world.update, 1.0/config.updates_per_second)
    pyglet.clock.schedule_interval(draw, 1.0/config.frames_per_second)
##    pyglet.clock.schedule(draw)
    pyglet.app.run()

# override app event loop to prevent unwanted draws
def idle():
    pyglet.clock.tick(poll=True)
    return pyglet.clock.get_sleep_time(sleep_idle=True)

main()