import math
import sys
from datetime import timedelta, datetime
import transaction
import argparse
import psycopg2
from psycopg2.errors import SerializationFailure
import random
import time
import os

TXN_ID = {
    "NEW_ORDER": "N",
    "PAYMENT": "P",
    "DELIVERY": "D",
    "ORDER_STATUS": "O",
    "STOCK_LEVEL": "S",
    "POPULAR_ITEM": "I",
    "TOP_BALANCE": "T",
    "RELATED_CUSTOMER": "R",
}

def get_percentile(data, percentile):
    size = len(data)
    return data[int(math.ceil((size * percentile) / 100)) - 1]

class MetricsManager:
    def __init__(self):
        # Keeps track of time taken for each transaction in milliseconds
        self.transaction_timings = []
        self.total_time = timedelta(0)

    # Takes in a timedelta for a transaction
    def add(self, delta):
        self.transaction_timings.append(delta / timedelta(milliseconds=1))

    def add_total_time(self, delta):
        self.total_time = delta

    def output_metrics(self):
        total_transactions = len(self.transaction_timings)
        sorted_transaction_timings = sorted(self.transaction_timings)
        total_transaction_timings = sum(sorted_transaction_timings)
        sys.stderr.write("Number of executed transactions: {}\n".format(total_transactions))
        sys.stderr.write("Total transaction execution time (in seconds): {}\n".format(self.total_time.total_seconds()))
        sys.stderr.write(
            "Transaction throughput (transactions / s): {}\n".format(
                total_transactions / (self.total_time.total_seconds())))
        sys.stderr.write(
            "Average transaction latency (in milliseconds): {}\n".format(
                total_transaction_timings / total_transactions))
        sys.stderr.write(
            "Median transaction latency (in milliseconds): {}\n".format(get_percentile(sorted_transaction_timings, 50)))
        sys.stderr.write("95th percentile transaction latency (in milliseconds): {}\n".format(
            get_percentile(sorted_transaction_timings, 95)))
        sys.stderr.write("99th percentile transaction latency (in milliseconds): {}\n".format(
            get_percentile(sorted_transaction_timings, 99)))

    def write_metrics(self, filename):
        metrics = []
        total_transactions = len(self.transaction_timings)
        sorted_transaction_timings = sorted(self.transaction_timings)
        total_transaction_timings = sum(sorted_transaction_timings)
        metrics.append(str(total_transactions))
        metrics.append(str(self.total_time.total_seconds()))
        metrics.append(str(total_transactions / (self.total_time.total_seconds())))
        metrics.append(str(total_transaction_timings / total_transactions))
        metrics.append(str(get_percentile(sorted_transaction_timings, 50)))
        metrics.append(str(get_percentile(sorted_transaction_timings, 95)))
        metrics.append(str(get_percentile(sorted_transaction_timings, 99)))
        with open(filename, "w") as f:
            f.write(",".join(metrics))

def setup_transactions(file, conn):
    transactions = []
    line = file.readline()
    while line:
        args = line.rstrip('\n').split(',')
        identifier = args[0]
        if identifier == TXN_ID["NEW_ORDER"]:
            inputs = args[1:]
            # Adds each item as a nested list
            num_items = int(args[-1])
            for i in range(0, num_items):
                item_line = file.readline()
                inputs.append(item_line.rstrip('\n').split(','))
            transactions.append(transaction.NewOrderTransaction(conn, inputs))
        elif identifier == TXN_ID["PAYMENT"]:
            transactions.append(transaction.PaymentTransaction(conn, args[1:]))
        elif identifier == TXN_ID["DELIVERY"]:
            transactions.append(transaction.DeliveryTransaction(conn, args[1:]))
        elif identifier == TXN_ID["ORDER_STATUS"]:
            transactions.append(transaction.OrderStatusTransaction(conn, args[1:]))
        elif identifier == TXN_ID["STOCK_LEVEL"]:
            transactions.append(transaction.StockLevelTransaction(conn, args[1:]))
        elif identifier == TXN_ID["POPULAR_ITEM"]:
            transactions.append(transaction.PopularItemTransaction(conn, args[1:]))
        elif identifier == TXN_ID["TOP_BALANCE"]:
            transactions.append(transaction.TopBalanceTransaction(conn, args[1:]))
        elif identifier == TXN_ID["RELATED_CUSTOMER"]:
            transactions.append(transaction.RelatedCustomerTransaction(conn, args[1:]))
        else:
            print("UNABLE TO PARSE XACT INPUT: ", args)

        line = file.readline()

    return transactions

def main():
    # Usage: python3 client.py <file> <hostNum> <db>
    # Example: python3 client.py 1.txt -hn 2
    # if hostNum not specified, use default host 2 (i.e. xcnc2)
    parser = argparse.ArgumentParser()
    parser.add_argument("file", metavar="F",
                        type=argparse.FileType('r'),
                        help='Transaction file for client')
    parser.add_argument("-hn", '--hostNum',
                        type=int, default=2,
                        help='Host number e.g. 2 for xcnc2. Default is xcnc2.'
                        )
    parser.add_argument("-p", '--port',
                        type=int, default=26260,
                        help='Port e.g. 26260. Default is 26260.'
                        )
    parser.add_argument("-db", '--database',
                        type=str, default="project",
                        help='Database name'
                        )
    args = parser.parse_args()

    host = f'xcnc{args.hostNum}.comp.nus.edu.sg'
    port = args.port
    user = 'root'  # use root so we don't have to grant privileges manually
    database = args.database
    conn = psycopg2.connect(host=host,
                            port=port,
                            user=user,
                            database=database)

    metrics = MetricsManager()
    transactions = setup_transactions(args.file, conn)
    metrics_filename = os.path.splitext(os.path.basename(args.file.name))[0] + ".metrics"
    args.file.close()

    total_execution_start = datetime.now()
    for txn in transactions:
        transaction_start = datetime.now()
        retry_count = 0
        last_error = ""
        while True:
            try:
                txn.run()
                # Flag issue if transactions had retried more than 15 times (sleep time > 5 seconds)
                if retry_count > 10:
                    sys.stderr.write("Transaction of type " + txn.__class__.__name__ + " retried: " + str(retry_count)
                                     + " times before completion with error: " + last_error + " \n")
                break
            except SerializationFailure as e:
                sleep_ms = (2 ** (retry_count % 16)) * 0.1 * (random.random() + 0.5)
                time.sleep(sleep_ms / 1000)
                last_error = str(e)
                retry_count += 1
                continue
        transaction_end = datetime.now()
        metrics.add(transaction_end - transaction_start)

        txn.print_outputs()
    total_execution_end = datetime.now()
    metrics.add_total_time(total_execution_end - total_execution_start)

    metrics.output_metrics()
    metrics.write_metrics(metrics_filename)
    conn.close()

if __name__ == '__main__':
    main()


import logging
import re
import time
import urllib.parse
import uuid
from datetime import datetime

from odoo import _, api, fields, models, tools
from odoo.exceptions import AccessError
from odoo.tools import email_split

_logger = logging.getLogger(__name__)

EVENT_OPEN_DELTA = 10  # seconds
EVENT_CLICK_DELTA = 5  # seconds

class MailTrackingEmail(models.Model):
    _name = "mail.tracking.email"
    _order = "time desc"
    _rec_name = "display_name"
    _description = "MailTracking email"

    # This table is going to grow fast and to infinite, so we index:
    # - name: Search in tree view
    # - time: default order fields
    # - recipient_address: Used for email_store calculation (non-store)
    # - state: Search and group_by in tree view
    name = fields.Char(string="Subject", readonly=True, index=True)
    display_name = fields.Char(
        string="Display name",
        readonly=True,
        store=True,
        compute="_compute_tracking_display_name",
    )
    timestamp = fields.Float(
        string="UTC timestamp", readonly=True, digits="MailTracking Timestamp"
    )
    time = fields.Datetime(string="Time", readonly=True, index=True)
    date = fields.Date(
        string="Date", readonly=True, compute="_compute_date", store=True
    )
    mail_message_id = fields.Many2one(
        string="Message", comodel_name="mail.message", readonly=True, index=True
    )
    message_id = fields.Char(compute="_compute_message_id")
    mail_id = fields.Many2one(string="Email", comodel_name="mail.mail", readonly=True)
    partner_id = fields.Many2one(
        string="Partner", comodel_name="res.partner", readonly=True
    )
    recipient = fields.Char(string="Recipient email", readonly=True)
    recipient_address = fields.Char(
        string="Recipient email address",
        readonly=True,
        store=True,
        compute="_compute_recipient_address",
        index=True,
    )
    sender = fields.Char(string="Sender email", readonly=True)
    state = fields.Selection(
        [
            ("error", "Error"),
            ("deferred", "Deferred"),
            ("sent", "Sent"),
            ("delivered", "Delivered"),
            ("opened", "Opened"),
            ("rejected", "Rejected"),
            ("spam", "Spam"),
            ("unsub", "Unsubscribed"),
            ("bounced", "Bounced"),
            ("soft-bounced", "Soft bounced"),
        ],
        string="State",
        index=True,
        readonly=True,
        default=False,
        help=" * The 'Error' status indicates that there was an error "
        "when trying to sent the email, for example, "
        "'No valid recipient'\n"
        " * The 'Sent' status indicates that message was succesfully "
        "sent via outgoing email server (SMTP).\n"
        " * The 'Delivered' status indicates that message was "
        "succesfully delivered to recipient Mail Exchange (MX) server.\n"
        " * The 'Opened' status indicates that message was opened or "
        "clicked by recipient.\n"
        " * The 'Rejected' status indicates that recipient email "
        "address is blacklisted by outgoing email server (SMTP). "
        "It is recomended to delete this email address.\n"
        " * The 'Spam' status indicates that outgoing email "
        "server (SMTP) consider this message as spam.\n"
        " * The 'Unsubscribed' status indicates that recipient has "
        "requested to be unsubscribed from this message.\n"
        " * The 'Bounced' status indicates that message was bounced "
        "by recipient Mail Exchange (MX) server.\n"
        " * The 'Soft bounced' status indicates that message was soft "
        "bounced by recipient Mail Exchange (MX) server.\n",
    )
    error_smtp_server = fields.Char(string="Error SMTP server", readonly=True)
    error_type = fields.Char(string="Error type", readonly=True)
    error_description = fields.Char(string="Error description", readonly=True)
    bounce_type = fields.Char(string="Bounce type", readonly=True)
    bounce_description = fields.Char(string="Bounce description", readonly=True)
    tracking_event_ids = fields.One2many(
        string="Tracking events",
        comodel_name="mail.tracking.event",
        inverse_name="tracking_email_id",
        readonly=True,
    )
    # Token isn't generated here to have compatibility with older trackings.
    # New trackings have token and older not
    token = fields.Char(
        string="Security Token",
        readonly=True,
        default=lambda s: uuid.uuid4().hex,
        groups="base.group_system",
    )

    @api.depends("mail_message_id")
    def _compute_message_id(self):
        """This helper field will allow us to map the message_id from either the linked
        mail.message or a mass.mailing mail.trace.
        """
        self.message_id = False
        for tracking in self.filtered("mail_message_id"):
            tracking.message_id = tracking.mail_message_id.message_id

    @api.model_create_multi
    def create(self, vals_list):
        records = super().create(vals_list)
        failed_states = self.env["mail.message"].get_failed_states()
        records.filtered(lambda one: one.state in failed_states).mapped(
            "mail_message_id"
        ).write({"mail_tracking_needs_action": True})
        return records

    def write(self, vals):
        super().write(vals)
        state = vals.get("state")
        if state and state in self.env["mail.message"].get_failed_states():
            self.mapped("mail_message_id").write({"mail_tracking_needs_action": True})

    def _find_allowed_tracking_ids(self):
        """Filter trackings based on related records ACLs"""
        # Admins passby this filter
        if not self or self.env.user.has_group("base.group_system"):
            return self.ids
        # Override ORM to get the values directly
        self._cr.execute(
            """
            SELECT id, mail_message_id, partner_id
            FROM mail_tracking_email WHERE id IN %s
            """,
            (tuple(self.ids),),
        )
        msg_linked = self._cr.fetchall()
        if not msg_linked:
            return []
        _, msg_ids, partner_ids = zip(*msg_linked)
        # Filter messages with their ACL rules avoiding False values fetched in the set
        msg_ids = self.env["mail.message"]._search(
            [("id", "in", [x for x in msg_ids if x])]
        )
        partner_ids = self.env["res.partner"]._search(
            [("id", "in", [x for x in partner_ids if x])]
        )
        return [
            track_id
            for track_id, mail_msg_id, partner_id in msg_linked
            if (mail_msg_id in msg_ids)  # We can read the linked message
            or (
                not mail_msg_id and partner_id in partner_ids
            )  # No linked mail.message but we can read the linked partner
            or (not any({mail_msg_id, partner_id}))  # No linked record
        ]

    @api.model
    def _search(
        self,
        args,
        offset=0,
        limit=None,
        order=None,
        count=False,
        access_rights_uid=None,
    ):
        """Filter ids based on related records ACLs"""
        ids = super()._search(
            args, offset, limit, order, count=count, access_rights_uid=access_rights_uid
        )
        if not self.env.user.has_group("base.group_system") and not count:
            ids = self.browse(ids)._find_allowed_tracking_ids()
        return ids

    def check_access_rule(self, operation):
        """Rely on related messages ACLs"""
        super().check_access_rule(operation)
        allowed_ids = self._search([("id", "in", self._find_allowed_tracking_ids())])
        disallowed_ids = set(self.exists().ids).difference(set(allowed_ids))
        if not disallowed_ids:
            return
        raise AccessError(
            _(
                "The requested operation cannot be completed due to security "
                "restrictions. Please contact your system administrator.\n\n"
                "(Document type: %s, Operation: %s)"
            )
            % (self._description, operation)
            + " - ({} {}, {} {})".format(
                _("Records:"), list(disallowed_ids), _("User:"), self._uid
            )
        )

    def read(self, fields=None, load="_classic_read"):
        """Override to explicitly call check_access_rule, that is not called
        by the ORM. It instead directly fetches ir.rules and apply them.
        """
        if not self.env.user.has_group("base.group_system"):
            self.check_access_rule("read")
        return super().read(fields=fields, load=load)

    @api.model
    def email_is_bounced(self, email):
        if not email:
            return False
        res = self.sudo()._email_last_tracking_state(email)
        return res and res[0].get("state", "") in {
            "rejected",
            "error",
            "spam",
            "bounced",
        }

    @api.model
    def _email_last_tracking_state(self, email):
        return self.search_read(
            [("recipient_address", "=", email.lower())],
            ["state"],
            limit=1,
            order="time DESC",
        )

    @api.model
    def email_score_from_email(self, email):
        if not email:
            return 0.0
        data = self.sudo().read_group(
            [("recipient_address", "=", email.lower())],
            ["recipient_address", "state"],
            ["state"],
        )
        mapped_data = {state["state"]: state["state_count"] for state in data}
        return self.with_context(mt_states=mapped_data).sudo().email_score()

    @api.model
    def _email_score_weights(self):
        """Default email score weights. Ready to be inherited"""
        return {
            "error": -50.0,
            "rejected": -25.0,
            "spam": -25.0,
            "bounced": -25.0,
            "soft-bounced": -10.0,
            "unsub": -10.0,
            "delivered": 1.0,
            "opened": 5.0,
        }

    def email_score(self):
        """Default email score algorimth. Ready to be inherited
        It can receive a recordset or mapped states dictionary via context.
        Must return a value beetwen 0.0 and 100.0
        - Bad reputation: Value between 0 and 50.0
        - Unknown reputation: Value 50.0
        - Good reputation: Value between 50.0 and 100.0
        """
        weights = self._email_score_weights()
        score = 50.0
        states = self.env.context.get("mt_states", False)
        if states:
            for state in states.keys():
                score += weights.get(state, 0.0) * states[state]
        else:
            for tracking in self:
                score += weights.get(tracking.state, 0.0)
        if score > 100.0:
            score = 100.0
        elif score < 0.0:
            score = 0.0
        return score

    @api.depends("recipient")
    def _compute_recipient_address(self):
        for email in self:
            email.recipient_address = False
            recipient_email = email_split(email.recipient)
            if recipient_email:
                email.recipient_address = recipient_email[0].lower()

    @api.depends("name", "recipient")
    def _compute_tracking_display_name(self):
        for email in self:
            parts = [email.name or ""]
            if email.recipient:
                parts.append(email.recipient)
            email.display_name = " - ".join(parts)

    @api.depends("time")
    def _compute_date(self):
        for email in self:
            email.date = fields.Date.to_string(fields.Date.from_string(email.time))

    def _get_mail_tracking_img(self):
        m_config = self.env["ir.config_parameter"]
        base_url = m_config.get_param("mail_tracking.base.url") or m_config.get_param(
            "web.base.url"
        )
        if self.token:
            path_url = (
                "mail/tracking/open/%(db)s/%(tracking_email_id)s/%(token)s/"
                "blank.gif"
                % {
                    "db": self.env.cr.dbname,
                    "tracking_email_id": self.id,
                    "token": self.token,
                }
            )
        else:
            # This is here for compatibility with older records
            path_url = "mail/tracking/open/{db}/{tracking_email_id}/blank.gif".format(
                db=self.env.cr.dbname, tracking_email_id=self.id
            )
        track_url = urllib.parse.urljoin(base_url, path_url)
        return (
            '<img src="%(url)s" alt="" '
            'data-odoo-tracking-email="%(tracking_email_id)s"/>'
            % {"url": track_url, "tracking_email_id": self.id}
        )

    def _partners_email_bounced_set(self, reason, event=None):
        recipients = []
        if event and event.recipient_address:
            recipients.append(event.recipient_address)
        else:
            recipients = [x for x in self.mapped("recipient_address") if x]
        for recipient in recipients:
            self.env["res.partner"].search(
                [("email", "=ilike", recipient)]
            ).email_bounced_set(self, reason, event=event)

    def smtp_error(self, mail_server, smtp_server, exception):
        values = {"state": "error"}
        IrMailServer = self.env["ir.mail_server"]
        if (
            str(exception) == IrMailServer.NO_VALID_RECIPIENT
            and not self.recipient_address
        ):
            values.update(
                {
                    "error_type": "no_recipient",
                    "error_description": "The partner doesn't have a defined email",
                }
            )
        else:
            values.update(
                {
                    "error_smtp_server": tools.ustr(smtp_server),
                    "error_type": exception.__class__.__name__,
                    "error_description": tools.ustr(exception),
                }
            )
            self.sudo()._partners_email_bounced_set("error")
        self.sudo().write(values)

    def tracking_img_add(self, email):
        self.ensure_one()
        tracking_url = self._get_mail_tracking_img()
        if tracking_url:
            content = email.get("body", "")
            content = re.sub(
                r'<img[^>]*data-odoo-tracking-email=["\'][0-9]*["\'][^>]*>', "", content
            )
            body = tools.append_content_to_html(
                content, tracking_url, plaintext=False, container_tag="div"
            )
            email["body"] = body
        return email

    def _message_partners_check(self, message, message_id):
        if not self.mail_message_id.exists():  # pragma: no cover
            return True
        mail_message = self.mail_message_id
        partners = mail_message.notified_partner_ids | mail_message.partner_ids
        if self.partner_id and self.partner_id not in partners:
            # If mail_message haven't tracking partner, then
            # add it in order to see his tracking status in chatter
            if mail_message.subtype_id:
                mail_message.sudo().write(
                    {"notified_partner_ids": [(4, self.partner_id.id)]}
                )
            else:
                mail_message.sudo().write({"partner_ids": [(4, self.partner_id.id)]})
        return True

    def _tracking_sent_prepare(self, mail_server, smtp_server, message, message_id):
        self.ensure_one()
        ts = time.time()
        dt = datetime.utcfromtimestamp(ts)
        self._message_partners_check(message, message_id)
        self.sudo().write({"state": "sent"})
        return {
            "recipient": message["To"],
            "timestamp": "%.6f" % ts,
            "time": fields.Datetime.to_string(dt),
            "tracking_email_id": self.id,
            "event_type": "sent",
            "smtp_server": smtp_server,
        }

    def _event_prepare(self, event_type, metadata):
        self.ensure_one()
        m_event = self.env["mail.tracking.event"]
        method = getattr(m_event, "process_" + event_type, None)
        if method and callable(method):
            return method(self, metadata)
        else:  # pragma: no cover
            _logger.info("Unknown event type: %s" % event_type)
        return False

    def _concurrent_events(self, event_type, metadata):
        m_event = self.env["mail.tracking.event"]
        self.ensure_one()
        concurrent_event_ids = False
        if event_type in {"open", "click"}:
            ts = metadata.get("timestamp", time.time())
            delta = EVENT_OPEN_DELTA if event_type == "open" else EVENT_CLICK_DELTA
            domain = [
                ("timestamp", ">=", ts - delta),
                ("timestamp", "<=", ts + delta),
                ("tracking_email_id", "=", self.id),
                ("event_type", "=", event_type),
            ]
            if event_type == "click":
                domain.append(("url", "=", metadata.get("url", False)))
            concurrent_event_ids = m_event.search(domain)
        return concurrent_event_ids

    def event_create(self, event_type, metadata):
        event_ids = self.env["mail.tracking.event"]
        for tracking_email in self:
            other_ids = tracking_email._concurrent_events(event_type, metadata)
            if not other_ids:
                vals = tracking_email._event_prepare(event_type, metadata)
                if vals:
                    events = event_ids.sudo().create(vals)
                    if event_type in {"hard_bounce", "spam", "reject"}:
                        for event in events:
                            self.sudo()._partners_email_bounced_set(
                                event_type, event=event
                            )
                    event_ids += events
            else:
                _logger.debug("Concurrent event '%s' discarded", event_type)
        return event_ids

    # TODO Remove useless method
    @api.model
    def event_process(self, request, post, metadata, event_type=None):
        # Generic event process hook, inherit it and
        # - return 'OK' if processed
        # - return 'NONE' if this request is not for you
        # - return 'ERROR' if any error
        return "NONE"  # pragma: no cover


import torch
from torch import nn
from torch.nn import functional as F

from models.networkLib._utils import _SimpleSegmentationModel

__all__ = ["DeepLabV3"]

class DeepLabV3(_SimpleSegmentationModel):
    """
    Implements DeepLabV3 model from
    `"Rethinking Atrous Convolution for Semantic Image Segmentation"
    <https://arxiv.org/abs/1706.05587>`_.

    Arguments:
        backbone (nn.Module): the network used to compute the features for the model.
            The backbone should return an OrderedDict[Tensor], with the key being
            "out" for the last feature map used, and "aux" if an auxiliary classifier
            is used.
        classifier (nn.Module): module that takes the "out" element returned from
            the backbone and returns a dense prediction.
        aux_classifier (nn.Module, optional): auxiliary classifier used during training
    """
    pass

class DeepLabHeadV3Plus(nn.Module):
    def __init__(self, in_channels, num_classes, low_level_channels=256, aspp_dilate=[12, 24, 36]):
        super(DeepLabHeadV3Plus, self).__init__()
        self.project = nn.Sequential(
            nn.Conv2d(low_level_channels, 48, 1, bias=False),
            nn.BatchNorm2d(48),
            nn.ReLU(inplace=True),
        )

        self.aspp = ASPP(in_channels, aspp_dilate)

        self.classifier = nn.Sequential(
            nn.Conv2d(304, 256, 3, padding=1, bias=False),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, num_classes, 1)
        )
        self._init_weight()

    def forward(self, feature):
        low_level_feature = self.project(feature['low_level'])
        output_feature = self.aspp(feature['out'])
        output_feature = F.interpolate(output_feature, size=low_level_feature.shape[2:], mode='bilinear',
                                       align_corners=False)
        return self.classifier(torch.cat([low_level_feature, output_feature], dim=1))

    def _init_weight(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

class DeepLabHead(nn.Module):
    def __init__(self, in_channels, num_classes, aspp_dilate=[12, 24, 36]):
        super(DeepLabHead, self).__init__()

        self.classifier = nn.Sequential(
            ASPP(in_channels, aspp_dilate),
            nn.Conv2d(256, 256, 3, padding=1, bias=False),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, num_classes, 1)
        )
        self._init_weight()

    def forward(self, feature):
        return self.classifier(feature['out'])

    def _init_weight(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

class ASPPConv(nn.Sequential):
    def __init__(self, in_channels, out_channels, dilation):
        modules = [
            nn.Conv2d(in_channels, out_channels, 3, padding=dilation, dilation=dilation, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU()
        ]
        super(ASPPConv, self).__init__(*modules)

class ASPPPooling(nn.Sequential):
    def __init__(self, in_channels, out_channels):
        super(ASPPPooling, self).__init__(
            nn.AdaptiveAvgPool2d(1),
            nn.Conv2d(in_channels, out_channels, 1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU())

    def forward(self, x):
        size = x.shape[-2:]
        for mod in self:
            x = mod(x)
        return F.interpolate(x, size=size, mode='bilinear', align_corners=False)

class ASPP(nn.Module):
    def __init__(self, in_channels, atrous_rates, out_channels=256):
        super(ASPP, self).__init__()
        modules = []
        modules.append(nn.Sequential(
            nn.Conv2d(in_channels, out_channels, 1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU()))

        rates = tuple(atrous_rates)
        for rate in rates:
            modules.append(ASPPConv(in_channels, out_channels, rate))

        modules.append(ASPPPooling(in_channels, out_channels))

        self.convs = nn.ModuleList(modules)

        self.project = nn.Sequential(
            nn.Conv2d(len(self.convs) * out_channels, out_channels, 1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(),
            nn.Dropout(0.5))

    def forward(self, x):
        res = []
        for conv in self.convs:
            res.append(conv(x))
        res = torch.cat(res, dim=1)
        return self.project(res)


# This is a sample Python script.

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.

from pulp import *

if __name__ == '__main__':
        a11 = 16
        a12 = 32
        a13 = 32
        a21 = 9
        a22 = 4
        a23 = 6
        a31 = 40
        a32 = 60
        a33 = 44
        b1 = 290
        b2 = 80
        b3 = 530
        c1 = 10
        c2 = 12
        c3 = 11
        # variables
        vol1 = pulp.LpVariable('vol1', 0)
        vol2 = pulp.LpVariable('vol2', 0)
        vol3 = pulp.LpVariable('vol3', 0)
        # function
        problem = pulp.LpProblem('0', LpMaximize)
        profit = vol1 * c1 + vol2 * c2 + vol3 * c3
        problem += profit, "Function"
        # constraints
        # ограничение на кол-во балласта
        problem += vol2 <= 8
        problem += vol3 <= 5
        # ограничение ресурсов экскаватора
        exccost = 0
        exccost += vol1 * a11
        exccost += vol2 * a12
        exccost += vol3 * a13
        problem += exccost <= b1
        # ограничение ресурсов бульдозера
        buldcost = 0
        buldcost += vol1 * a21
        buldcost += vol2 * a22
        buldcost += vol3 * a23
        problem += buldcost <= b2
        # ограничение трудовых ресурсов
        buldcost = 0
        buldcost += vol1 * a31
        buldcost += vol2 * a32
        buldcost += vol3 * a33
        problem += buldcost <= b3
        # solving problem
        status = problem.solve()
        print(status)
        print("Результат:")
        for variable in problem.variables():
            print(variable.name, "=", variable.varValue)
        print("Прибыль:")
        print(value(problem.objective))

# See PyCharm help at https://www.jetbrains.com/help/pycharm/


import numpy
import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph.dockarea import *
from pyqtgraph.dockarea import *
from pyqtgraph.parametertree import ParameterTree
from color_dock import *
import logging
from . import planeVector
from ndviewer_options import *

nameToPattern = {
    'SolidPattern': QtCore.Qt.SolidPattern,
    'LinearGradientPattern': QtCore.Qt.LinearGradientPattern,
    'Dense1Pattern': QtCore.Qt.Dense1Pattern,
    'Dense2Pattern': QtCore.Qt.Dense2Pattern,
    'Dense3Pattern': QtCore.Qt.Dense3Pattern,
    'Dense4Pattern': QtCore.Qt.Dense4Pattern,
    'Dense5Pattern': QtCore.Qt.Dense5Pattern,
    'Dense6Pattern': QtCore.Qt.Dense6Pattern,
    'Dense7Pattern': QtCore.Qt.Dense7Pattern,
    'NoBrush': QtCore.Qt.NoBrush,
    'HorPattern': QtCore.Qt.HorPattern,
    'VerPattern': QtCore.Qt.VerPattern,
    'CrossPattern': QtCore.Qt.CrossPattern,
    'BDiagPattern': QtCore.Qt.BDiagPattern,
    'FDiagPattern': QtCore.Qt.FDiagPattern,
    'DiagCrossPattern': QtCore.Qt.DiagCrossPattern
}

class TiledViewBox(pg.ViewBox):
    def __init__(self, ndViewer, plane, parent=None):

        pv = ndViewer.paramValue
        self.bgType  = pv(('ViewBox Options','ViewBox Background','bg-type'))
        self.bgColor1 = pv(('ViewBox Options','ViewBox Background','bg-color 1'))
        self.bgColor2 = pv(('ViewBox Options','ViewBox Background','bg-color 2'))
        super(TiledViewBox,self).__init__(invertY=True,lockAspect=True)
        self.ndViewer = ndViewer
        self.plane = plane
        # connect background parameters
        for c in ndViewer.param('ViewBox Options','ViewBox Background').children():
            c.sigValueChanged.connect(self.backgroundChanged)

        # rect changed
        self.sigXRangeChanged.connect(self.rectChanged)
        self.sigYRangeChanged.connect(self.rectChanged)

        self.updateBackground()
    def rectChanged(self):
        self.updateBackground()

    def backgroundChanged(self, param, changes):
        pv = self.ndViewer.paramValue
        self.bgType  = pv(('ViewBox Options','ViewBox Background','bg-type'))
        self.bgColor1 = pv(('ViewBox Options','ViewBox Background','bg-color 1'))
        self.bgColor2 = pv(('ViewBox Options','ViewBox Background','bg-color 2'))

        self.updateBackground()

    def updateBackground(self):
        self.background.show()
        self.background.setVisible(True)
        if self.bgType == 'LinearGradientPattern':
            g =  QtGui.QLinearGradient(
                                       QtCore.QRectF(self.rect()).topLeft(),
                                       QtCore.QRectF(self.rect()).bottomLeft()
            )
            g.setColorAt(0, self.bgColor1);
            g.setColorAt(1, self.bgColor2);
            brush = QtGui.QBrush(g)
        else:
            brush = QtGui.QBrush()
            brush.setStyle(nameToPattern[str(self.bgType)])
            brush.setColor(self.bgColor1)

        self.background.setBrush(brush)
    def setBackgroundColor(self):
        self.updateBackground()

class ViewBoxWidget(QtGui.QWidget):
    def __init__(self,ndViewer, plane, parent=None):

        super(ViewBoxWidget,self).__init__(parent=parent)
        self.mainLayout = QtGui.QVBoxLayout()
        self.glayout = pg.GraphicsLayout()
        self.graphicsView = pg.GraphicsView()

        # the view box
        self.viewBox = TiledViewBox(ndViewer=ndViewer, plane=plane)

        # the scales
        self.xAxis = pg.AxisItem(orientation='bottom', linkView=self.viewBox)
        self.yAxis = pg.AxisItem(orientation='left', linkView=self.viewBox)
        #self.xScale.setLabel(text=aixsLetters[viewAxis[0]])
        #@self.yScale.setLabel(text=aixsLetters[viewAxis[1]])

        # parameter interaction
        p = ndViewer.param('ViewBox Options','Show Axis')
        def showAxisChanged(param, changes):
            if changes == True:
                self.glayout.addItem(self.xAxis,2,1)
                self.glayout.addItem(self.yAxis,1,0)
            else:
                self.glayout.removeItem(self.xAxis)
                self.glayout.removeItem(self.yAxis)
        p.sigValueChanged.connect(showAxisChanged)

        def setupUI():
            # set the main layout
            self.setLayout(self.mainLayout)

            # make the graphics-view  the central widget
            self.mainLayout.addWidget(self.graphicsView)

            # add layout to graphics-view
            self.graphicsView.setCentralItem(self.glayout)

            # add items to layout
            self.glayout.addItem(self.viewBox,1,1)

        #  layouting etc
        setupUI()

class OpenGl3dWidget(QtGui.QWidget):
    def __init__(self, ndViewer):
        super(OpenGl3dWidget,self).__init__()
        self.ndViewer = ndViewer

        def _setupUI():
            self.mainLayout = QtGui.QHBoxLayout()
            self.setLayout(self.mainLayout)
        _setupUI()

class LayerStackCtrlWidget(QtGui.QWidget):
    def __init__(self, ndViewer):
        super(LayerStackCtrlWidget,self).__init__()
        self.ndViewer = ndViewer

        def _setupUI():
            self.mainLayout = QtGui.QHBoxLayout()
            self.setLayout(self.mainLayout)
        _setupUI()

class SettingsEditorDialog(QtGui.QDialog):

    def __init__(self,ndViewer, parent):
        super(SettingsEditorDialog, self).__init__(parent)

        self.resize(800,600)
        self.ndViewer = ndViewer
        self.layout = QtGui.QVBoxLayout()
        self.setLayout(self.layout)

        self.paramTree = ParameterTree()
        self.paramTree.setParameters(self.ndViewer.options.p, showTop=False)
        self.layout.addWidget(self.paramTree)
    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.hide()
            event.accept()
        else:
            super(QtGui.QDialog, self).keyPressEvent(event)

# http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
class NdViewerCtrlWidget(QtGui.QWidget,object):
    def __init__(self, ndViewer):
        super(NdViewerCtrlWidget,self).__init__()
        self.ndViewer = ndViewer
        self._setupUI()
        self.diag = SettingsEditorDialog(ndViewer=self.ndViewer,parent=self)
    def _setupUI(self):
        self.mainLayout = QtGui.QHBoxLayout()
        self.setLayout(self.mainLayout)

        exitAction = QtGui.QAction(QtGui.QIcon.fromTheme("accessories-text-editor"), 'Settings', self)
        #exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Settings editor')
        exitAction.triggered.connect(self.showSettingsEditor)

        toolbar = QtGui.QToolBar()
        toolbar.addAction(exitAction)
        toolbar.setIconSize(QtCore.QSize(20,20))
        self.mainLayout.addWidget(toolbar)

    def showSettingsEditor(self):
        self.diag.show()

class NdViewerWidget(QtGui.QWidget):
    def __init__(self, globalAxis):
        super(NdViewerWidget,self).__init__()

        # global options object
        self.options = NdViewerOptions(ndViewer=self)

        self.globalAxis = globalAxis
        self.nDim = len(self.globalAxis)
        self.shape = self.globalAxis.shape
        self.planes = planeVector([])

        # widgets
        self.area = MyDockArea()
        self.area.setParent(self)
        self.ctrlWidget = NdViewerCtrlWidget(self)
        self.layerStackCtrlWidget = LayerStackCtrlWidget(ndViewer=self)
        self.layerDock = ColorDock(name="Layers", color=(50,)*3)

        self._setupUI()

    def _setupUI(self):
        self.mainLayout = QtGui.QVBoxLayout()
        self.setLayout(self.mainLayout)
        self.mainLayout.addWidget(self.ctrlWidget)
        self.mainLayout.addWidget(self.area)
        self.layerDock.addWidget(self.layerStackCtrlWidget)
        self.area.addDock(self.layerDock,'right')

    def paramValue(self, key):
        """ get the params value"""
        return self.options[key]

    def param(self, *args, **kwargs):
        """ get the param object"""
        return self.options.param(*args,**kwargs)

    def addOrthogonalPlaneViewer(self, plane, pos=None, otherDock=None):

        # add the plane
        plane.planeIndex = len(self.planes)
        self.planes.addPlane(plane)

        # color of the dock
        color = self._getDockColor(plane)
        # add the dock
        dock = ColorDock(name=plane.name, color=color)
        dock.setParent(self.area)
        # plance the dock
        self._placeDock(dock=dock, pos=pos, otherDock=otherDock)

        # the widget
        viewBoxWidget = ViewBoxWidget(ndViewer=self,plane=plane, parent=dock)
        dock.addWidget(viewBoxWidget)

        return dock

    def add3dViewer(self):
        if(self.ndim <= 2):
            raise RuntimeError("3d viewer needs at least 3 axis")
        assert  False

    def _getDockColor(self, plane):
        """ Helping function to get the color of the dock
        """
        color = (0,0,0)
        if plane.zAxis != -1:
            color = self.globalAxis[plane.zAxis].color[0:3]
        return color

    def _placeDock(self, dock, pos=None, otherDock=None):
        """ Helping function to place the docks
        """
        if otherDock is not None and pos is not None:
            self.area.addDock(dock,pos,otherDock)
        elif pos is not None:
            self.area.addDock(dock,pos,otherDock)
        else:
            self.area.addDock(dock)
        return dock


import cv2
import numpy as np

img = cv2.imread('../img/taekwonv1.jpg')
img_draw = img.copy()
mask = np.zeros(img.shape[:2], dtype=np.uint8)  # 마스크 생성
rect = [0,0,0,0]    # 사각형 영역 좌표 초기화
mode = cv2.GC_EVAL  # 그랩컷 초기 모드
# 배경 및 전경 모델 버퍼
bgdmodel = np.zeros((1,65),np.float64)
fgdmodel = np.zeros((1,65),np.float64)

# 마우스 이벤트 처리 함수
def onMouse(event, x, y, flags, param):
    global mouse_mode, rect, mask, mode
    if event == cv2.EVENT_LBUTTONDOWN : # 왼쪽 마우스 누름
        if flags <= 1: # 아무 키도 안 눌렀으면
            mode = cv2.GC_INIT_WITH_RECT # 드래그 시작, 사각형 모드 ---①
            rect[:2] = x, y # 시작 좌표 저장
    # 마우스가 움직이고 왼쪽 버튼이 눌러진 상태
    elif event == cv2.EVENT_MOUSEMOVE and flags & cv2.EVENT_FLAG_LBUTTON :
        if mode == cv2.GC_INIT_WITH_RECT: # 드래그 진행 중 ---②
            img_temp = img.copy()
            # 드래그 사각형 화면에 표시
            cv2.rectangle(img_temp, (rect[0], rect[1]), (x, y), (0,255,0), 2)
            cv2.imshow('img', img_temp)
        elif flags > 1: # 키가 눌러진 상태
            mode = cv2.GC_INIT_WITH_MASK    # 마스크 모드 ---③
            if flags & cv2.EVENT_FLAG_CTRLKEY :# 컨트롤 키, 분명한 전경
                # 흰색 점 화면에 표시
                cv2.circle(img_draw,(x,y),3, (255,255,255),-1)
                # 마스크에 GC_FGD로 채우기      ---④
                cv2.circle(mask,(x,y),3, cv2.GC_FGD,-1)
            if flags & cv2.EVENT_FLAG_SHIFTKEY : # 쉬프트키, 분명한 배경
                # 검정색 점 화면에 표시
                cv2.circle(img_draw,(x,y),3, (0,0,0),-1)
                # 마스크에 GC_BGD로 채우기      ---⑤
                cv2.circle(mask,(x,y),3, cv2.GC_BGD,-1)
            cv2.imshow('img', img_draw) # 그려진 모습 화면에 출력
    elif event == cv2.EVENT_LBUTTONUP: # 마우스 왼쪽 버튼 뗀 상태 ---⑥
        if mode == cv2.GC_INIT_WITH_RECT : # 사각형 그리기 종료
            rect[2:] =x, y # 사각형 마지막 좌표 수집
            # 사각형 그려서 화면에 출력 ---⑦
            cv2.rectangle(img_draw, (rect[0], rect[1]), (x, y), (255,0,0), 2)
            cv2.imshow('img', img_draw)
        # 그랩컷 적용 ---⑧
        cv2.grabCut(img, mask, tuple(rect), bgdmodel, fgdmodel, 1, mode)
        img2 = img.copy()
        # 마스크에 확실한 배경, 아마도 배경으로 표시된 영역을 0으로 채우기
        img2[(mask==cv2.GC_BGD) | (mask==cv2.GC_PR_BGD)] = 0
        cv2.imshow('grabcut', img2) # 최종 결과 출력
        mode = cv2.GC_EVAL # 그랩컷 모드 리셋
# 초기 화면 출력 및 마우스 이벤트 등록
cv2.imshow('img', img)
cv2.setMouseCallback('img', onMouse)
while True:
    if cv2.waitKey(0) & 0xFF == 27 : # esc
        break
cv2.destroyAllWindows()


# Q42. Find the sum of number digits in List.
# The original list is : [12, 67, 98, 34]
# List Integer Summation : [3, 13, 17, 7]

def my_function():
    list=[12, 67, 98, 34]
    i=0
    b=[]
    while i<len(list):
        a=list[i]%10
        list[i]=list[i]//10
        b.append(a+list[i])
        i=i+1
    print(b)
my_function()


class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        # thoughts:
        # push two word into two dict
        # check if they are the same

        # new thoughts:
        # list(word) can push word all chars into list
        # sort it, that two anagrams should have the same lists
        # create a table with the sorted list as key, and push all same ones
        # into its value

        word_table = {}
        for word in strs:
            word_sorted = "".join(sorted(list(word)))
            if word_sorted not in word_table:
                word_table[word_sorted] = [word]
            else:
                word_table[word_sorted].append(word)
        return list(word_table.values())


from keras.models import Model
from keras.layers.convolutional import Convolution2D, MaxPooling2D, Conv2DTranspose
from keras.layers import Input
from keras.layers import Lambda
from keras.layers import concatenate

class ConvolutionalNeuralNetwork:

    def get_model(self,
                  input_dimentions,
                  metrics,
                  configuration
                  ):

        total_deconvolutional_filters = self._get_deconvolutional_filters(configuration.total_convolutional_layers,
                                                                          configuration.total_convolutional_filters)
        total_deconvolutional_layers = configuration.total_convolutional_layers - 1

        inputs = Input(input_dimentions)
        input_layer = Lambda(lambda x: x / 255)(inputs)

        convolutional_layers = self._setup_convolution(input_layer, configuration)
        last_layer = self._setup_deconvolution(convolutional_layers, total_deconvolutional_layers,
                                               total_deconvolutional_filters, configuration)

        outputs = Convolution2D(1, (1, 1), activation=configuration.output_activation)(last_layer)

        model = Model(inputs=[inputs], outputs=[outputs])
        model.compile(optimizer=configuration.optimizer, loss=configuration.loss, metrics=metrics)

        return model

    def _setup_convolution(self, input_layer, configuration):

        previous_layer = input_layer

        convolutional_layers = []

        total_filters = configuration.total_convolutional_filters

        for layer_index in range(0, configuration.total_convolutional_layers):
            convolutional_layer = Convolution2D(total_filters, configuration.filter_size_convolution,
                                                activation=configuration.activation, padding=configuration.padding)(previous_layer)
            convolutional_layer = Convolution2D(total_filters, configuration.filter_size_convolution,
                                                activation=configuration.activation, padding=configuration.padding)(convolutional_layer)

            if layer_index < configuration.total_convolutional_layers - 1:
                pooling_layer = MaxPooling2D(configuration.pool_size)(convolutional_layer)
                previous_layer = pooling_layer
            else:
                previous_layer = convolutional_layer

            convolutional_layers.append(convolutional_layer)
            total_filters = total_filters * 2

        return convolutional_layers

    def _setup_deconvolution(self, covolutional_layers, total_layers, total_filters, configuration):

        previous_layer = covolutional_layers[len(covolutional_layers) - 1]

        deconvolutional_layers = []
        merged_layers = []

        for layer_index in range(0, total_layers):
            merged_layer = Conv2DTranspose(total_filters, configuration.filter_size_deconvolution,
                                           strides=configuration.strides, padding=configuration.padding)(previous_layer)
            convolution_to_reverse = covolutional_layers[len(covolutional_layers) - layer_index - 2]
            merged_layer = concatenate([merged_layer, convolution_to_reverse])

            convolution_layer = Convolution2D(total_filters, configuration.filter_size_convolution, activation=configuration.activation,
                                              padding=configuration.padding)(merged_layer)
            convolution_layer = Convolution2D(total_filters, configuration.filter_size_convolution, activation=configuration.activation,
                                              padding=configuration.padding)(convolution_layer)

            deconvolutional_layers.append(convolution_layer)
            merged_layers.append(merged_layer)

            previous_layer = merged_layer
            total_filters = int(total_filters / 2)

        last_layer = previous_layer

        return last_layer

    def _get_deconvolutional_filters(self, total_convolutional_layers, total_convolutional_filters):
        return [i * 2 ** exp
                for exp in range(0, total_convolutional_layers - 1)
                for i in range(total_convolutional_filters, total_convolutional_filters + 1)][-1]


#Accending List
from array import *
def is_ascending (item):
    flag = True
    i = 1
    while i < len(item):
        if(item[i] <= item[i - 1]):
            flag = False
        i += 1
    return flag

#Riffle
import math
from array import *
def riffle(items, out=True):
    part2=math.floor(len(item)/2)
    i=0
    while(part2<len(item)):
        if(out==True):
            print(item[i],item[part2])
        else:
            print(item[part2],item[i])
        i+=1;
        part2+=1;

#Only_odd_digits
def only_odd_digits(n):
    integers=str(n)
    flag=True
    for i in integers:
        if(int(i)%2==0):
            flag=False
    return flag

#Cyclops Numbers
import math
def is_cyclops(n):
    num=str(n)
    flag=True
    mid=math.floor(len(num)/2)
    if(num[mid]!='0'):
        flag=False
        return flag
    else:
        if(len(num)==1):
            flag=True
            return flag
        else:
            i=0
            mid+=1
            while(mid<len(num)):
                if((num[i]=='0') or (num[mid]=='0')):
                    flag=False
                    return flag
                i+=1
                mid+=1
                return flag

#Domino Cycle
def domino_cycle(tiles):
    length=len(tiles)
    flag=0
    for i in range(0,1):
        if(tiles[i][1]!=tiles[(i+1)%length][0]):
            flag=1
            break
        return (True if flag==0 else False)

def count_growlers(animals):
  #this variable stores the growling count
  count = 0
  #traversing through the list
  for j,i in enumerate(animals):
    temp = 0
    #if is is cat or dog that means we have to check left of the list
    if(i == 'cat' or i == 'dog'):
      #checking from left of the i
      for k in range(j):
      #if it is a dog or god temp is incremented
        if(animals[j-k-1] == 'dog' or animals[j-k-1] == 'god'):
              temp += 1
        #if any element is a cat or a tac tem is decreased
        else:
            temp -= 1
    #same here if i is tac or god we will check to the right of it
    elif(i == 'tac' or i == 'god'):
  #checking for dog or god and incrementing temp
      for k in range(len(animals) - j-1):
        if(animals[j+k+1] == 'dog' or animals[j+k+1] == 'god'):
              temp += 1
        #decrementing if cat or tac is found
        else:
              temp -= 1
  #if after checking more number of dog or god were found the temp would be greater than zero

    count += 1

    #count is increased

  #returning the count
  return count

def scylla_or_charybdis(sequence, n):
    #steps follows the distnace travelled from the begining and will help determine when youve fallen off
    steps = 0
    #count follows the amount of moves it takes to fall off
    count = 0
    #print(f"You have n - 1 = {n-1} step(s) in both directions before you fall off")
    k = 1
    j = 1
    items = {}
    while k != len(sequence) // 2 + 1:
        #since steps is guaranteed to end with 2n consecutve steps to the right
        for i in range(0, len(sequence), k):
            if j*k-1 > len(sequence)-1:
                k += 1
                steps = 0
                count = 0
                j = 1
                break
            if sequence[j*k-1] == "+":
                steps += 1
                count += 1
                j += 1
            elif sequence[j*k-1] == "-":
                steps -= 1
                count += 1
                j += 1
            if abs(steps) == n:
                items[k] = count
                #print(f"You have fallen due to reaching step {steps}")
                #print(f"using every {k}th step of the sequence it took only {count} moves to fall off")
                k = k + 1
                steps = 0
                count = 0
                j = 1
                break

#Count Dominators
def count_dominators(items):
    i=0
    count=1
    if(len(items)==0):
        return 0
    elif(len(items)==1):
        return count
    else:
        while(i<len(items)-1):
            Yes=True
            j=i+1
            while(j<len(items)):
                if(items[i]<=items[j]):
                    Yes=False
                    break
                j+=1
            if(Yes==True):
                count+=1
            i+=1
        return count

#Extracting Increasing integers form digit string
def extract_increasing(digits):
    count=1
    res=[]
    if(len(digits)==0):
        return
    elif(len(digits)==1):
        return res.append(int(digits(0)))
    else:
        for idx in range(0, len(digits), count):
            if(int(digits)):
                digits='1'
    extract_increasing(digits)

def words_with_letters(words, letters):
    result=[]

    for letter in letters:
        words_list=[]
        letter=list(letter)   #converting the letter to list of characters

        for word in words:   #checking all words
            letter_index=0   #index of letter
            equal_count=0   #no of characters of the letter found in the word
            word=list(word)   #converting the word to list of characters

            for i in range(0,len(word)):
                if(word[i]==letter[letter_index]):
                    if(letter_index==len(letter)-1):   #if all characters of letter found in word
                        words_list.append("".join(word))   #convert list to string and append to the words_list
                        break
                    equal_count+=1
                    letter_index+=1
        result.append(words_list)   #append the words list to the final result list
    return result

#Running Median of Three
def running_median_of_three(items):
    result = []
    for i in range(len(items)):
        if i < 2:
            result.append(items[i])
        else:
            if (items[i - 2] <= items[i - 1] <= items[i]) or (items[i] <= items[i - 1] <= items[i - 2]):
                median = items[i - 1]
            elif (items[i - 2] <= items[i] <= items[i - 1]) or (items[i - 1] <= items[i] <= items[i - 2]):
                median = items[i]
            else:
                median = items[i - 2]
            result.append(median)
    return result

#The card that wins the trick
def winning_card(cards,trump = None):
    rank_dict={'deuce':2, 'trey':3, 'four':4, 'five':5, 'six':6,'seven':7,
    'eight':8, 'nine':9, 'ten':10, 'jack':11, 'queen':12, 'king':13,
    'ace':14}
    if trump is not None:
        selected_cards = [card for card in cards if card[1]==trump]
    else:
        selected_cards = cards
        max_card = max(selected_cards,key = lambda card: rank_dict[card[0]])
    return max_card

#Boustrophedon
def create_zigzag(rows, cols, start = 1):
    result = []
    for i in range(rows):
        tmp = []
        for j in range(cols):
            tmp.append(start)
            start += 1
        if(i%2==1):
            tmp.reverse()
        result.append(tmp)
    return result

#Multidimentional Knight Moves
def move(knight, start, end):
    new_knight = []
    length = len(knight)
    for i in range(length):
        new_knight.append(knight[i])
    for i in range(length):
        value = abs(start[i] - end[i])
        if value in new_knight:
            new_knight.remove(value)
            continue
        else:
            return False
    return True

def winning_card(card,trump=None):

  #list to determine rank of card
  l=["one","two","three","four","five","six","seven","eight","nine","ten","jack","queen","king","ace"]

  #if trump is none, take trump as first card type
  if trump == None:
    s = card[0][1]
    #first card will be trump
    p=0
  else:
    s = trump
    #find position of first trump card
    for i in range(len(card)):
      if card[i][1] == s:
        p=i
        break

  #iterate through the card list
  #which card has higher power in the type of trump
  for i in range(len(card)):
    if card[i][1] == s:
      #greater index defines higher rank
      if l.index(card[i][0]) >= l.index(card[p][0]):
        p = i

  #print the card
  print(card[p])

#Seven ruls, zeros Drool
def seven_zero(n):
    d = 1
    ans = 0
    while True:
        if n%2 == 0 or n%5 == 0:
            k = 1
            while k <= d:
                val = int(k * '7' + (d-k) * '0')
                if val%n == 0:
                    ans = val
                    break
                k += 1
        else:
            val = int(d * '7')
            ans = val if val%n == 0 else 0
        d += 1
        if ans > 0:
             return ans
             break

#Sum of the balls off the brass monkey
def phyramid_block(n,m,h):
  sum1=0;
  for i in range(h):
    sum1=sum1+(n*m)
    n=n+1
    m=m+1
  return sum1

#FulCrum
def can_balance(items):
    if len(items)==1:
        return 0
    for i in range(1,len(items)-2):
        lans = 0
        for j in range(i):
            lans = lans + (i-j)*items[j]
        rans = 0
        for j in range(i+1,len(items)):
            rans = rans + (j-i)*items[j]
        if lans == rans:
            return i
    return -1

#Last Man standing
def find_seq(n,k):
        mens=[(i+1) for i in range(n)]
        st=0
        seq=[]
        sz=n
        while len(mens)>1:
                st=(st+(k-1))%sz
                sz-=1
                seq.append(mens[st])
                del mens[st]
        seq.append(mens[0])
        return seq

def three_summers(items, goal):
    for i in range(len(items)):
        for j in range(i + 1, len(items)):
            for k in range(j + 1, len(items)):
                if items[i] + items[j] + items[k] == goal:
                    return True
    return False

def crag_score(dice):
    dice.sort()
    if dice[0]==dice[1] and dice[1]==dice[2]:return 25
    elif sum(dice)==13:
        if dice[0]==dice[1] or dice[0]==dice[2] or dice[1]==dice[2]:
            return 50
        else:
            return 26
    elif dice[0]==4 and dice[1]==5 and dice[2]==6:return 20
    elif dice[0]==1 and dice[1]==2 and dice[2]==3:return 20
    elif dice[0]==2 and dice[1]==4 and dice[2]==6:return 20
    elif dice[0]==1 and dice[1]==3 and dice[2]==5:return 20
    else : return max(dice)

#Longest palindrome substring
def longest_palindrome(text):
    longest = ''
    for i in range(len(text)):
        for j in range(i+1, len(text)+1):
            if text[i:j][::-1] == text[i:j]:
                if len(text[i:j]) > len(longest):
                    longest = text[i:j]
    return longest

#All your fractions are belong to base
def group_and_skip(n,out,inp):
    remainingCoin=[]
    while(n>out):
        n=n-out
        remainingCoin.append(n%out)
        n=int(n/out)+inp
    remainingCoin.append(n)
    print(remainingCoin)

import statistics

def tukeys_ninthers(items):

    #Declare sublists as type of list

    sublists = []

    #calculate length of items

    lengthofitems = len(items)

    #Iterate loop

    for each in range(0,lengthofitems,3):

        #append to sublists list

        sublists.append(items[each:each+3])

    #Declare medianNewList as type of list

    medianNewList = []

    #Iterate loop

    for eachsub_list in sublists:

        #calculate medianValue value

        medianValue = int(statistics.median(eachsub_list))

        #append the element medianValue to medianNewList

        medianNewList.append(medianValue)

    #calculate lengthofmedian

    lengthofmedian = len(medianNewList)

    #check lengthofmedian is less than equal to 3

    if lengthofmedian <= 3:

        turnElement = statistics.median(medianNewList)
        return turnElement

    else:

        #call recursive approach

        turnElement = tukeys_ninthers(medianNewList)

        return turnElement

#Recaman's Sequence
def recman(n):
    sequence=[1]
    hash_set=set([1])
    for i in range(1,n,1):
        val=sequence[i-1]-(i+1)
        if val<=0 or val in hash_set:
            val=sequence[i-1]+i+1
        sequence.append(val)
        hash_set.add(val)
    return sequence

def fibonacci_word(k):

    if k==0:
        return '0'
    if k==1:
        return '1'

    a="0"
    b="1"
    result=""

    while len(result)!=k+1:

        result=result+a+b
        a=b
        b=result

        if len(result)>=k:
            break

    return result[k]

#Rversing the Reversed
def reverse_reversed(items):

    reversed = []

    for item in items[::-1]:

        if isinstance(item, list):

            reversed.append(reverse_reversed(item))

        else:

            reversed.append(item)

    return reversed

#count word dominators
# definition of the function.

def dominates(w1, w2):

    cnt = 0

    # start the for loop

    for (c1, c2) in zip(w1, w2):

        # check the condition.

        if c1 > c2:

             # update the value.

            cnt += 1

    # return the value.

    return cnt > len(w1)/2

# definition of the function.

def count_word_dominator(words):

    cnt = 0

    for i in range(len(words)):

        dominateStatus = True

        for j in range(i + 1, len(words)):

            if not dominates(words[i], words[j]):

                dominateStatus = False

        if dominateStatus:

            cnt += 1

    return cnt

#Duplicates digit bonus
def rpt(n):
    x=list(n)
    x.reverse()
    f=0
    cost=0
    k=-1

    last=x[0]

    #looks for repeating digits
    for i in range(len(x)):

        k=-1
        if x[i]=='#':
            continue
        c=x[i]
        x[i]='#'
        #Calculate the value for the repeating digits
        for j in range(i+1,len(x)):

            if x[j]==c:
                k=k+1
                x[j]='#'
            else:
                break
        if k>=0:
            if f==0 and c==last:
                cost+=(2*(10)*k)
                f=1
            else:
                cost=cost+((10)*k)

    return cost

#Nearest Smaller element

def nearest_smaller(items):

    result = []

    curr_ele_index = 0

    while len(result) < len(items):

        smallest_ele_left = 'x'

        smallest_ele_right = 'x'

        left_index = curr_ele_index - 1

        while left_index >= 0:

            if(items[left_index] < items[curr_ele_index]):

                smallest_ele_left = left_index

                break

            left_index -= 1

        right_index = curr_ele_index + 1

        while right_index < len(items):

            if(items[right_index] < items[curr_ele_index]):

                smallest_ele_right = right_index

                break

            right_index += 1

        if smallest_ele_left == 'x':

            if smallest_ele_right == 'x':

                result.append(items[curr_ele_index])

            else:

                result.append(items[smallest_ele_right])

        else:

            if smallest_ele_right == 'x':

                result.append(items[smallest_ele_left])

            else:

                dist_left = curr_ele_index - smallest_ele_left

                dist_right = smallest_ele_right - curr_ele_index

                if(dist_left < dist_right):

                    result.append(items[smallest_ele_left])

                elif (dist_right < dist_left):

                    result.append(items[smallest_ele_right])

                else:

                    smaller_ele = min(items[smallest_ele_left], items[smallest_ele_right])

                    result.append(smaller_ele)

        curr_ele_index += 1

    return result

def collatzy_distance(start, end):
    currentSeq = [start]

    dis = 0
    while end not in currentSeq:
        nextSeq = []
        for c in currentSeq:
            nextSeq.append(3*c + 1)
            nextSeq.append(c//2)
        currentSeq = nextSeq
        dis += 1

    return dis

#Interesting Intersecting
def squares_intersect(s1,s2):
    x1,x2,y1,y2,r1,r2=s1[0],s2[0],s1[1],s2[1],s1[2],s2[2]
    if ((x1<x2) and (x1+r1<x2)) or ((y1<y2) and (y1+r1<y2)):
        return False
    elif ((x2<x1) and (x2+r2<x1)) or ((y2<y1) and (y2+r2<y1)):
        return False
    else:
        return True

def give_change(amount, coins):
    change = [] # store the change
    j = 0; # to index the coins
    while amount > 0:
        if amount >= coins[j]:
            amount = amount - coins[j]
            change.append(coins[j])
        else:
            j = j + 1
    return change

#Keep doubling
def double_untill_all_digits(n, giveup = 1000):
    counter = 0
    numbers = [0,1,2,3,4,5,6,7,8,9]
    digits = [int(i) for i in str(n)]
    for i in range(giveup):
        if all(elem in digits for elem in numbers):
            return counter
        else:
            n = n * 2
            counter += 1
            digits = [int(i) for i in str(n)]
            return -1

#that's enough of you
def remove_kth(items,k):

    unique_items = list(set(items))
    count_items = [0]*len(unique_items)
    final_items = []
    for i in range(len(items)):
        ind = unique_items.index(items[i])
        count_items[ind] = count_items[ind]+1
        if count_items[ind]<=k:
            final_items = final_items + [items[i]]
        return final_items

#Longest palindrome substring
def longest_palindrome(text):
    longest = ''
    for i in range(len(text)):
        for j in range(i+1, len(text)+1):
            if text[i:j][::-1] == text[i:j]:
                if len(text[i:j]) > len(longest):
                    longest = text[i:j]
    return longest

#All your fractions are belong to base
def group_and_skip(n,out,inp):
    remainingCoin=[]
    while(n>out):
        n=n-out
        remainingCoin.append(n%out)
        n=int(n/out)+inp
    remainingCoin.append(n)
    print(remainingCoin)

#Count Consective summers
def count_consecutive_summers(n):
    count = 0
    for i in range(1, n+1):
        total = 0
        for j in range(i, n+1):
            total += j
            if total == n:
                count += 1
    return count

#Pulldonw Your Neighbours
def eliminate_neighbours( items ):
    items_list = items.copy( )
    size = len( items_list )
    larger = 0; smaller = items[0]
    neighbour = 0
    small_ind = 0;
    count = 0
    for i in items_list:
        if i > larger:
            larger = i
        if ( larger <= size ):
            while ( larger in items_list ):
                size = len( items_list )
                for j in items_list:
                    if j < smaller:
                        smaller = j
                        small_ind = items_list.index( j )
                    if ( small_ind == 0 ):
                        neighbour = items_list[ small_ind +1 ]
                    elif ( small_ind == size-1 ):
                        neighbour = items_list [ small_ind -1 ]
                    else:
                        if ( items_list[ small_ind -1 ] > items_list [ small_ind +1 ] ):
                            neighbour = items_list[ small_ind -1 ]
                        else:
                            neighbour = items_list[ small_ind +1 ]
            items_list.remove( smaller )
            items_list.remove( neighbour )
            count += 1
            smaller = larger
        return ( count )

#What do you hear, what do you say
def count_and_say(digits):

    if len(digits) ==0:

        return ""

    digitletters =[]

    digitCount = []

    previousDigit =''

    count =-1

    for digit in digits:

        if digit == previousDigit:

            digitCount[count] += 1

        else:
            digitletters.append(digit)

            count +=1

            digitCount.append(1)

            previousDigit = digit

    output =""

    count =0

    for digit in digitletters:

        output+= str(digitCount[count]) + digit

        count +=1

    return output

def safe_squares_rooks(n,rooks):
    #creating two sets for storing unsafe rows and cols
    unsafe_rows=set()
    unsafe_cols=set()
    #looping through rooks list
    for rook in rooks:
        #adding current rook's row and col to unsafe row,col sets
        #sets prevent duplicates, so we dont need to worry about them
        unsafe_rows.add(rook[0])
        unsafe_cols.add(rook[1])
    #number of safe rows or cols = n - number of unsafe rows or cols
    safe_rows_count=n-len(unsafe_rows)
    safe_cols_count=n-len(unsafe_cols)
    #returning total number of safe spaces
    return safe_rows_count*safe_cols_count

#Bishops on a binge
def safe_squares_bishops(n, bishops):
    safe_cells = 0
    for row in range(n):
        for col in range(n):
            safe=True
            for pos in bishops:
                if abs(row - pos[0]) == abs(col - pos[1]):
                    safe=False
                    break
            if safe:safe_cells+=1
    return safe_cells

#Up for the count
def counting_series(n):
    len = 1;
    count = 9;
    start = 1;
    while n > len * count :
        n= n - len * count
        len = len + 1
        count = count * 10
        start = start * 10
    start = start + (n - 1) / len
    s = str(start)
    return s[((n - 1) % len)]

#Reverse the vowels
def reverse_vowels(text):
    vowels=[]
    for c in text:
        i=c.lower()
        if i=='a' or i=='e' or i=='i' or i=='o' or i=='u':
            vowels.append(c)
    vowels.reverse()
    k=0
    str=""
    for j in range(len(text)):
        i=text[j].lower()
        if i=='a' or i=='e' or i=='i' or i=='o' or i=='u':
            st=str+vowels[k]
            k+=1
        else:
            str=str.text[j]
    str=str[0].upper()+str[1:]
    return str

#Everybody do a scrooge shuffle
def spread_the_coins(pile,left,right):
    start=i=0
    while i <(len(pile)):
        k = pile[i]//(left+right)
        if k>0:
            pile[i]-=k*(left+right)
            if i!=len(pile)-1:
                pile[i+1] += k*right
            else:
                pile.append(k*right)
            if i!=0:
                pile[i-1] += k*left
                i-=1
                continue
            else:
                start-=1
                pile.insert(0,k*left)
                continue
        i+=1
    return(start,pile)

#Calking-wilf sequence
def calkin_wilf(n):

    queue=[]
    f=Fraction(1,1)
    queue.append(f)
    ans=Fraction(1,1)
    while n>0:
        temp=queue.pop(0)

        ans=temp

        p=temp.numerator
        q=temp.denominator

        f1=Fraction(p,p+q)
        f2=Fraction(p+q,q)

        queue.append(f1)
        queue.append(f2)
        n=n-1
        return ans

#Hippity hoppity, abolish loopity
def frog_collision(frog1, frog2):
    s1_x, s1_y, d1_x, d1_y = frog1[0], frog1[1], frog1[2], frog1[3]
    s2_x, s2_y, d2_x, d2_y = frog2[0], frog2[1], frog2[2], frog2[3]
    try:
        t1 = (s1_x - s2_x)/(d2_x - d1_x)
        t2 = (s1_y - s2_y)/(d2_y - d1_y)
        if t1 == t2:
            return t1
        else:
            return None
    except:
        if d2_x - d1_x == 0 and s1_x - s2_x == 0 and d2_y - d1_y != 0:
            return (s1_y - s2_y)/(d2_y - d1_y)
        elif d2_y - d1_y == 0 and s1_y - s2_y == 0 and d2_x - d1_x != 0:
            return (s1_x - s2_x)/(d2_x - d1_x)
        else:
            return None

#Double Trouble
def double_trouble(items,n):
    data = []
    counter,value = -1,1
    while True:
        counter = (counter+1)%len(items)
        data.append(value)
        if sum(data) >= n: break
        if counter == len(items) -1 : value *= 2
    return items[counter]

#Nearest Polygonal Number
def fun(s, i):
    return ((s-2)*(i*i) - (s-4)*i)//2

def nearest_poligonal_number(n, s):
    a = 1
    b = 2
    while fun(s, b) < n:
        b = b*b
    m = (a+b)//2
    while b-a >= 2:
        at_m = fun(s,m)
        if at_m > n:
            b = m
        elif at_m < n:
             a = m
        else:
            return at_m
        m = (a+b)//2
    at_a = fun(s,a)
    at_b = fun(s,b)
    if abs(at_a-n) <= abs(at_b-n):
            return at_a
    else:
            return at_b

#Postfix Interpreter
from collections import deque
def postfix_evaluate(items):
    stack = deque()

    for x in items:
        if x=='+' or x=='-' or x=='*' or x=='/':
            op1=stack.pop()
            op2=stack.pop()
            result=calculate(op2,op1,x)
            stack.append(result)
        else:
            stack.append(x)

    return stack.pop()

def calculate(op2,op1,x):
    if x is '*':
        return int(op2)*int(op1)
    elif x is '/':
        if (op1 == 0):
            return 0
        else:
            return int(op2)//int(op1)
    elif x is '+':
        return int(op2)+int(op1)
    elif x is '-':
        return int(op2)-int(op1)

#Fractran Interpreter
import sys
def fractran(n:int, prog:list, giveup:int=1000):
    # Intialize result list with n
    res = [n]
    # Run the logic, giveup number of times
    while giveup:
        # A flag which is set if a integer calue is found
        found = False
        for x in prog:
            frac = n * float(x[0])/float(x[1])
            # If n * fraction is an integer, add it to result list,
            # change n to this integer and break the loop to proceed to next iteration
            if frac.is_integer():
                n = int(frac)
                res.append(n)
                found = True
                break
        # If flag is not set terminate the program
        if not found:
            sys.exit(-1)
        giveup -= 1
    return res

#Permutation Cycyle
def is_permutation(items, n):
    found = []
    for i in range(1, n + 1):
        for j in range(0, len(items)):
            if items[j] == i:
                found.append(i)
                break
    if len(found) == n:
        return True
    else:
        return False

#Ztalloc ecneqes
def ztalloc(shape):
    prev=1

    for i in range(len(shape)-1,-1,-1):
        if(shape[i]=='d'):
            prev=prev*2
        else:
            prev=prev-1
            if((prev)%3!=0):
                return None
            else:
                prev=prev/3
                if(((prev)%2==0)):
                    return None
    return int(prev)

#Reverse ascending sublist
def reverse_ascending_sublists(items):
    low = 0
    final = []
    for i in range(1, len(items)-1):
        if items[i-1] >= items[i]:
            temp = items[low:i]
            final += temp[::-1]
            low = i
    temp = items[low:]
    if items[-1] > items[-2]:
        final += temp[::-1]
    else:
        final += temp
    return final

#Brangelina
def brangelina(first, second):
    vowels = ['a','e','i', 'o', 'u']
    fcnt = 0
    sindex = 0
    findex = 0
    flag = False
    for i in range(len(first)): # This for-loop counts the number of vowel groups
        if first[i] in vowels:
            if not flag:
                fcnt += 1
                findex = i      # set vowel group index
                flag = True
        else:
            flag = False

    if fcnt > 1:                # if vowel group count >0
        flag = False
        for i in range(findex-1, -1, -1):
            if first[i] in vowels:
                findex = i      # find the index of second last vowel group
                flag = True
            else:
                if flag:
                    break
    for i in range(len(second)):  # find the index of first vowel group
        if second[i] in vowels:
            break
        sindex +=1
    return(first[:findex] + second[sindex:]) # return combined word

#Uambcselne the word
def unscramble(words,word):
    guesses=[]
    for i in words:
        if i[0]==word[0] and i[-1]==word[-1] and len(word)==len(i):
            if sorted(list(word[1:-1]))==sorted(list(i[1:-1])):
                guesses.append(i)
    return guesses

def pancake_scramble(text):
#Initialize variable
    s = text
      #Loop from 2 to n
    for i in range(2,len(text)+1):
      #Reverse first i chars and keep the next chars as is
        s = s[:i][::-1]+s[i:]
    return s

#Substituition words
def substitution_words(seed):
    rng = random.Random(seed)
    f = open('words_alpha.txt', 'r', encoding='utf-8')
    words = [x.strip() for x in f]
    f.close()
    yield ('ABCD', words)
    for i in range(100):
        pat = ''
        ll = int(sqrt(rng.randint(3*3, 10*10)))
        n = rng.randint(1, ll)
        for j in range(ll):
            pat += ups[rng.randint(0, n - 1)]
            yield (pat, words)

#Mahattan Skyline
def manhattan_skyline(towers):
    if not towers:
        return []
    if len(towers) == 1:
        return [[towers[0][0], towers[0][2]], [towers[0][1], 0]]

    mid = len(towers) // 2
    left = manhattan_skyline(towers[:mid])
    right = manhattan_skyline(towers[mid:])
    return merge(left, right)

def merge(left, right):
    h1, h2 = 0, 0
    i, j = 0, 0
    result = []

    while i < len(left) and j < len(right):
        if left[i][0] < right[j][0]:
            h1 = left[i][1]
            corner = left[i][0]
            i += 1
        elif right[j][0] < left[i][0]:
            h2 = right[j][1]
            corner = right[j][0]
            j += 1
        else:
            h1 = left[i][1]
            h2 = right[j][1]
            corner = right[j][0]
            i += 1
            j += 1
        if is_valid(result, max(h1, h2)):
            result.append([corner, max(h1, h2)])
    result.extend(right[j:])
    result.extend(left[i:])
    return result

#Count Overlapping Disks
def count_overlapping_disks_generator(seed):
    rng = random.Random(seed)
    for n in range(3, 150):
        d = 2 * n
    for i in range(10):
        disks = set()
        while len(disks) < n:
            x = rng.randint(-d, d)
            y = rng.randint(-d, d)
            r = rng.randint(1, n)
            disks.add((x, y, r))
        disks = list(disks)
        disks.sort()
        yield (disks,)

#an Unordinary ordering of ordinary cardinals
def sort_by_digit_count(seed):
    rng = random.Random(seed)
    for k in range(1000):
        n = k + 2
        yield ([rng.randint(1, n * n * n * n) for i in range(n)],)

#Count Visible in range
def count_divisibles_in_range(start, end, n):

    count = 0;

    for i in range(start, end+1):
        if (i % n == 0):
            count = count + 1

    return count

#Bridge hand shape
def bridge_hand_shape(hand):
    count = [0]*4
    for x in hand:
        if x[1] == 'spades':
            count[0]+=1
        elif x[1] == 'hearts':
            count[1]+=1
        elif x[1] == 'diamonds':
            count[2]+=1
        else:
            count[3]+=1
    return count

#Milton work point count
def cards(hand, trump = 'notrump'):
    suits = {'spades':0,'hearts':0,'diamonds':0,'clubs':0}
    points = 0
    for card in hand:
        value, suit = card
        if value == 'ace':
            points += 4
        elif value == 'king':
            points += 3
        elif value == 'queen':
            points += 2
        elif value == 'jack':
            points += 1
        suits[suit] += 1
    if sorted(suits.values()) == [3, 3, 3, 4]:
        points -= 1
        return points
    for suit, n in suits.items():
        if n >= 7:
            points += 3
        elif n == 6:
            points += 2
        elif n == 5:
            points += 1
        if trump != 'notrump' and suit != trump:
            if n == 0:
                points += 5
            elif n == 1:
                points += 3
    return points

#Bulls and cows
def bullCow(orgNum,predNum,bulls,cows):
    bi = 0
    ci =0
    orgNum = str(orgNum)
    predNum = str(predNum)
    for i in range(4):
        if orgNum[i] == predNum[i]:
            bi += 1
        elif orgNum[i] in predNum:
            ci+=1
    return (bi==bulls) and(ci==cows)

#Sort by more frequency
from collections import Counter
def frequency_sort(elems):
    result = [item for items, c in Counter(elems).most_common()
    for item in [items] * c]
    return result

#Calling all units,BandE in progress
def is_perfect_power(n):
    if n <= 1:
        return True

    # loop from 2 to square root
    for i in range(2, int(n ** 0.5) + 1):
        f = i
        while f <= n:
            f = f * i

            if f == n:
                return True

    return False

#Fabonacci sum
def fibonacci_sum(n):
    first = 0
    second = 1
    fib_list = []
    values = []
    fib_list.append(first)
    fib_list.append(second)
    next = first + second
    # Constructing a list of fibonacci numbers upto n
    while next <= n:
        fib_list.append(next)
        first = second
        second = next
        next = first + second
    answer = 0
    fib_list.sort(reverse=True)
    # Taking a decision based on value of answer
    while True:
        for i in fib_list:
            if answer + i > n:
                continue
            else:
                values.append(i)
                answer = answer + i
                if answer == n:
                    break
                fib_list.remove(i)
        if answer == n:
            break

    return values

#Rocks with Friends
def rooks_with_friends(n,friends,enemies):
    chess=[]
    for i in range(n):
        temp=[]
        for j in range(n):
            temp.append(0)
        chess.append(temp)
    for x,y in friends:
        chess[x][y]=1
    for x,y in enemies:
        chess[x][y]=-1
    ans=0
    for i in range(n):
        for j in range(n):
            if chess[i][j]==1 or chess[i][j]==-1:
                continue
            f=0
            for k in range(j+1,n):
                if chess[i][k]==-1:
                    f=1
                    break
                if chess[i][k]==1:
                    break
                for k in range(j-1,-1,-1):
                    if chess[i][k]==-1:
                        f=1
                        break
                if chess[i][k]==1:
                    break
        for k in range(i-1,-1,-1):
            if chess[k][j]==-1:
                f=1
                break
            if chess[k][j]==1:
                break
        for k in range(i+1,n):
            if chess[k][j]==-1:
                f=1
                break
            if chess[k][j]==1:
                break
        if f==0:
            ans+=1
    return ans

#possible words in hangman
def possible_words_generator(seed):
  f = open('words_sorted.txt', 'r', encoding='utf-8')
  words = [x.strip() for x in f]
  f.close()
  rng = random.Random(seed)
  letters = 'abcdefghijklmnopqrstuvwxyz'
  for i in range(100):
    k = rng.randint(1, 10)
    guessed = set(rng.sample(letters, k))
    patword = rng.choice(words)
    pat = ''
    for ch in patword:
      pat += ch if ch in guessed else '*'
    yield (words, pat)

def get_word_shape(word ) :
    ## initialize the word shape to empty list
    wordShape = []
    ## iterate thru the word length
    for i in range( 1 , len(word ) ) :
        ## Compare the current with previous letter
        ## if the diff is > 0 append 1 to the word shape
        ## if the diff is < 0 append -1 to the word shape
        ## if the diff is 0 append 0 to the word shape
        ## ord(letter) returns the ascii code of that letter
        if ( ord(word[i]) - ord(word[i-1]) ) > 0 :
            wordShape.append(1)
        elif ( ord(word[i]) - ord(word[i-1]) ) < 0 :
            wordShape.append(-1)
        else :
            wordShape.append(0)
    ## finally return the wordshape
    return wordShape

#Factoring Factorials
def factoring_factorial(n):
  if n < 3:
    return [(n, 1)]
  count = dict()
  for i in range(1, n+1):
    f = prime_factors(i)
    for val in f:
      count[val] = count.get(val, 0) + 1
  ans = [(p,e) for p,e in count.items()]
  return ans

#Aliqout Sequence
def getProperDivisor(N):
  sum = 0
  for i in range(1, int(sqrt(N)) + 1):
    if N % i == 0:
      if N // i == i:
        sum += i
      else:
        sum += i
        sum += N // i
  return sum - N

def aliquot_sequence(n,giveup=100):
  lst = []
  lst.append(n);
  giveup=giveup-1
  while n > 0 and giveup>=1:
    n = getProperDivisor(n)
    if n in lst:
      break
    lst.append(n);
    giveup=giveup-1
  return lst;

def possible_words(words,pattern):
  matches=[]
  for word in words:
    isMatch=True
    if len(word)==len(pattern):
      for i in range(len(pattern)):
        if pattern[i].isalpha():
          if pattern[i]!=word[i]:
            isMatch=False
            break
          else:
            if word[i] in pattern:
              isMatch=False
              break
      if isMatch:
        matches.append(word)
  return matches

#All paths leads to Rome
def lattice_paths(m, n, tabu):
  if m == 0 and n == 0:
    return 1
  if m < 0 or n < 0:
    return 0
  x = [tup[0] for tup in tabu]
  y = [tup[1] for tup in tabu]
  if m in x and n in y:
    return 0
  return lattice_paths(m - 1, n, tabu) + lattice_paths(m, n - 1, tabu)

def collatzy_distance(start, end):
    currentSeq = [start]

    dis = 0
    while end not in currentSeq:
        nextSeq = []
        for c in currentSeq:
            nextSeq.append(3*c + 1)
            nextSeq.append(c//2)
        currentSeq = nextSeq
        dis += 1

    return dis

def isSorted(points):
  for i in range(0,len(points)-1):
    if points[i][0]+points[i][1]>points[i+1][0]+points[i+1][1]:
      return False
    return True
def count_maximal_layers(points):
  n=len(points)
  if n==0:
    return 0
  #Distance metric (x+y) based increasing sort
  if not isSorted(points):
    for i in range(0,n):
      for j in range(i+1,n):
        if points[i][0]+points[i][1]>points[j][0]+points[j][1]:
          points[i],points[j]=points[j],points[i]
  #New list containing non-maximal layer points
  non_maximal_layer_points=[]
  for i in range(0,n):
    for j in range(i+1,n):
      if points[i][0]<points[j][0] and points[i][1]<points[j][1]:
        non_maximal_layer_points.append(points[i])
        break
  #Add 1 in the final result of how many times maximal layer points should be removed
  return 1 + count_maximal_layers(non_maximal_layer_points)

def square_follows(it):
    result=[]
    prev_values=set()
    for num in it:
        sq_rt=num**0.5
        if sq_rt in prev_values:
            result.append(int(sq_rt))
        prev_values.add(num)
    return result

list = []
def sum_of_distinct_cubes(n):
    if n==0 :
        return True

    for x in range(n , 0, -1):
        if(sum_of_distinct_cubes(n - (x*x*x)) == True):
            if(x not in list):
                list.append(x)
                return True
    return False

if(sum_of_distinct_cubes(8) == True):
    print(list[::-1])
else:
    print(None)

def perimeter_limit_split(a,b,p):
  dp=[[None]*(b+1) for i in range(a+1)]#dp array for memoization
  return perimeter_limit_split_dp(a,b,p,dp)#calling helper function
  #helper function
def perimeter_limit_split_dp(a,b,p,dp):
  #base case
  if p>=2*(a+b):
    return 0
  m1=m2=M1=M2=0
  m=float('inf')
  #recurring cases
  if a>1:
    for i in range(1,a//2+1):
      m1 = perimeter_limit_split_dp(i,b,p,dp)if dp[i][b]==None else dp[i][b]
      m2 = perimeter_limit_split_dp(a-i,b,p,dp) if dp[a-i][b]==None else dp[a-i][b]
      m = min(1+m1+m2,m)
  if b>1:
    for i in range(1,b//2+1):
      m1 = perimeter_limit_split_dp(a,i,p,dp) if dp[a][i]==None else dp[a][i]
      m2 = perimeter_limit_split_dp(a,b-i,p,dp) if dp[a][b-i]==None else dp[a][b-i]
      m = min(1+m1+m2,m)
  #memoizing in dp table
  dp[a][b]=m
  return m

def van_eck(n):
  prev_index = [-1]*(n+1)
  curr_index = [-1]*(n+1)
  count = [0]*(n+1)
  arr = [0]

  curr_index[arr[-1]] = 0
  count[arr[-1]]+=1

  for i in range(1,n+1):
    if count[arr[-1]]==1:
      arr.append(0)
      count[arr[-1]]+=1
      prev_index[arr[-1]] = curr_index[arr[-1]]
      curr_index[arr[-1]] = len(arr)-1
    else:
      arr.append(curr_index[arr[-1]] - prev_index[arr[-1]])
      count[arr[-1]] +=1
      prev_index[arr[-1]] = curr_index[arr[-1]]
      curr_index[arr[-1]] = len(arr)-1
  return arr[-1]

def balanced_ternary(n):
   l=[]
   i=0
   while n!=0:
       if n%3==2 or n%3==-1:
           l.append(-1*pow(3,i))
       elif n%3==1 or n%3==-2:
           l.append(pow(3,i))
       if n<0:
           n=int((n-1)/3)
       else:
           n=int((n+1)/3)
       i+=1
   return l[::-1] #reversing order of exponents

def bulgarianSolitaire(piles, k, moves):

  reqdConfiguration = [i+1 for i in range(k)]

  piles.sort()

  if(piles == reqdConfiguration):

    return moves

  piles = [x - 1 for x in piles]

  piles.append(len(piles))

  piles = list(filter(lambda a: a != 0, piles))

  moves += 1

  # print(moves,piles)

  return bulgarianSolitaire(piles,k,moves)

def recurse(letters, n, tabu, cur_pos, current_string, final_list):
      # base case
    if cur_pos == n:
        final_list.append(current_string)
        return

      # we can put all the letters from letters in this position if it does not voilate the substring condition
    for letter in letters:
        new_str = current_string+letter
        allowed = True
        for item in tabu:
            if len(new_str) >= len(item):
                index = -1*len(item)
                if new_str[index:] == item:
                    allowed = False
                    break
        if allowed:
            recurse(letters, n, tabu, cur_pos+1, new_str, final_list)

def forbidden_substring(letters, n, tabu):
    letters = [char for char in letters]
    final_list = []
    letters.sort()
    recurse(letters, n, tabu, 0, "", final_list)
    return final_list

import numpy as np
import platform
def md ( a = 3, b = 10, n = 2, dt = 0.1 ):
    step_print_index = 0
    step_print_num = 10
    step_print = 0

    for step in range ( 0, step_num + 1 ):

        if ( step == 0 ):
            pos, vel, acc = initialize ( p_num, d_num )
        else:
            pos, vel, acc = update ( p_num, d_num, pos, vel, force, acc, mass, dt )

        force, potential, kinetic = compute ( p_num, d_num, pos, vel, mass )

        if ( step == 0 ):
            e0 = potential + kinetic

        if ( step == step_print ):
            rel = ( potential + kinetic - e0 ) / e0
            print ( '  %8d  %14f  %14f  %14g' % ( step, potential, kinetic, rel ) )
            step_print_index = step_print_index + 1
            step_print = ( step_print_index * step_num ) // step_print_num

    return

def unscramble(words,word):
    guesses=[]
    for i in words:
        if i[0]==word[0] and i[-1]==word[-1] and len(word)==len(i):
            if sorted(list(word[1:-1]))==sorted(list(i[1:-1])):
                guesses.append(i)
    return guesses

def eliminate_neighbours( items ):
    items_list = items.copy( )
    size = len( items_list )
    larger = 0; smaller = items[0]
    neighbour = 0
    small_ind = 0;
    count = 0
    for i in items_list:
        if i > larger:
            larger = i
        if ( larger <= size ):
            while ( larger in items_list ):
                size = len( items_list )
                for j in items_list:
                    if j < smaller:
                        smaller = j
                        small_ind = items_list.index( j )
                if ( small_ind == 0 ):
                    neighbour = items_list[ small_ind +1 ]
                elif ( small_ind == size-1 ):
                    neighbour = items_list [ small_ind -1 ]
                else:
                    if ( items_list[ small_ind -1 ] > items_list [ small_ind +1 ] ):
                        neighbour = items_list[ small_ind -1 ]
                    else:
                        neighbour = items_list[ small_ind +1 ]
                items_list.remove( smaller )
                items_list.remove( neighbour )
                count += 1
                smaller = larger
            return ( count )

def seven_zero(n):
    d = 1
    ans = 0
    while True:
        if n%2 == 0 or n%5 == 0:
            k = 1
            while k <= d:
                val = int(k * '7' + (d-k) * '0')
                if val%n == 0:
                    ans = val
                    break
                k += 1
        else:
            val = int(d * '7')
            ans = val if val%n == 0 else 0
        d += 1
        if ans > 0:
             return ans
             break

def running_median_of_three(items):
    result = []
    for i in range(len(items)):
        if i < 2:
            result.append(items[i])
        else:
            if (items[i - 2] <= items[i - 1] <= items[i]) or (items[i] <= items[i - 1] <= items[i - 2]):
                median = items[i - 1]
            elif (items[i - 2] <= items[i] <= items[i - 1]) or (items[i - 1] <= items[i] <= items[i - 2]):
                median = items[i]
            else:
                median = items[i - 2]
            result.append(median)
    return result

def only_odd_digits(n):
    integers=str(n)
    flag=True
    for i in integers:
        if(int(i)%2==0):
            flag=False
    return flag

def double_untill_all_digits(n, giveup = 1000):
    counter = 0
    numbers = [0,1,2,3,4,5,6,7,8,9]
    digits = [int(i) for i in str(n)]
    for i in range(giveup):
        if all(elem in digits for elem in numbers):
            return counter
        else:
            n = n * 2
            counter += 1
            digits = [int(i) for i in str(n)]
            return -1


from library import config
from library.lcd_comm import Orientation
from library.lcd_comm_rev_a import LcdCommRevA
from library.lcd_comm_rev_b import LcdCommRevB
from library.lcd_simulated import LcdSimulated
from library.log import logger

THEME_DATA = config.THEME_DATA
CONFIG_DATA = config.CONFIG_DATA

def _get_full_path(path, name):
    if name:
        return path + name
    else:
        return None

def _get_theme_orientation() -> Orientation:
    if THEME_DATA["display"]["DISPLAY_ORIENTATION"] == 'portrait':
        return Orientation.PORTRAIT
    elif THEME_DATA["display"]["DISPLAY_ORIENTATION"] == 'landscape':
        return Orientation.LANDSCAPE
    elif THEME_DATA["display"]["DISPLAY_ORIENTATION"] == 'reverse_portrait':
        return Orientation.REVERSE_PORTRAIT
    elif THEME_DATA["display"]["DISPLAY_ORIENTATION"] == 'reverse_landscape':
        return Orientation.REVERSE_LANDSCAPE
    else:
        logger.warning("Orientation '", THEME_DATA["display"]["DISPLAY_ORIENTATION"], "' unknown, using portrait")
        return Orientation.PORTRAIT

class Display:
    def __init__(self):
        self.lcd = None
        if CONFIG_DATA["display"]["REVISION"] == "A":
            self.lcd = LcdCommRevA(com_port=CONFIG_DATA['config']['COM_PORT'],
                                   display_width=CONFIG_DATA["display"]["DISPLAY_WIDTH"],
                                   display_height=CONFIG_DATA["display"]["DISPLAY_HEIGHT"],
                                   update_queue=config.update_queue)
        elif CONFIG_DATA["display"]["REVISION"] == "B":
            self.lcd = LcdCommRevB(com_port=CONFIG_DATA['config']['COM_PORT'],
                                   display_width=CONFIG_DATA["display"]["DISPLAY_WIDTH"],
                                   display_height=CONFIG_DATA["display"]["DISPLAY_HEIGHT"],
                                   update_queue=config.update_queue)
        elif CONFIG_DATA["display"]["REVISION"] == "SIMU":
            self.lcd = LcdSimulated(display_width=CONFIG_DATA["display"]["DISPLAY_WIDTH"],
                                    display_height=CONFIG_DATA["display"]["DISPLAY_HEIGHT"])
        else:
            logger.error("Unknown display revision '", CONFIG_DATA["display"]["REVISION"], "'")

    def initialize_display(self):
        # Reset screen in case it was in an unstable state (screen is also cleared)
        self.lcd.Reset()

        # Send initialization commands
        self.lcd.InitializeComm()

        # Set brightness
        self.lcd.SetBrightness(CONFIG_DATA["display"]["BRIGHTNESS"])

        # Set backplate RGB LED color (for supported HW only)
        self.lcd.SetBackplateLedColor(THEME_DATA['display'].get("DISPLAY_RGB_LED", (255, 255, 255)))

        # Set orientation
        self.lcd.SetOrientation(_get_theme_orientation())

    def display_static_images(self):
        if THEME_DATA['static_images']:
            for image in THEME_DATA['static_images']:
                logger.debug(f"Drawing Image: {image}")
                self.lcd.DisplayBitmap(
                    bitmap_path=THEME_DATA['PATH'] + THEME_DATA['static_images'][image].get("PATH"),
                    x=THEME_DATA['static_images'][image].get("X", 0),
                    y=THEME_DATA['static_images'][image].get("Y", 0),
                    width=THEME_DATA['static_images'][image].get("WIDTH", 0),
                    height=THEME_DATA['static_images'][image].get("HEIGHT", 0)
                )

    def display_static_text(self):
        if THEME_DATA['static_text']:
            for text in THEME_DATA['static_text']:
                logger.debug(f"Drawing Text: {text}")
                self.lcd.DisplayText(
                    text=THEME_DATA['static_text'][text].get("TEXT"),
                    x=THEME_DATA['static_text'][text].get("X", 0),
                    y=THEME_DATA['static_text'][text].get("Y", 0),
                    font=THEME_DATA['static_text'][text].get("FONT", "roboto-mono/RobotoMono-Regular.ttf"),
                    font_size=THEME_DATA['static_text'][text].get("FONT_SIZE", 10),
                    font_color=THEME_DATA['static_text'][text].get("FONT_COLOR", (0, 0, 0)),
                    background_color=THEME_DATA['static_text'][text].get("BACKGROUND_COLOR", (255, 255, 255)),
                    background_image=_get_full_path(THEME_DATA['PATH'],
                                                    THEME_DATA['static_text'][text].get("BACKGROUND_IMAGE", None))
                )

display = Display()