# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import pytz
from dateutil import rrule
import logging

from openerp import api, models, fields
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DTF
from openerp.tools.translate import _
from openerp.exceptions import ValidationError

from openerp.addons.resource.resource import seconds
import openerp.addons.decimal_precision as dp
from openerp.osv import fields as old_api_fields, osv

_logger = logging.getLogger(__name__)

SLOT_START_DELAY_MINS = 15
SLOT_DURATION_MINS = 60

class ResourceResource(models.Model):
    _inherit = 'resource.resource'

    to_calendar = fields.Boolean('Display on calendar')
    color = fields.Char('Color')
    has_slot_calendar = fields.Boolean('Use Working Time as Slots Definition')

class ResourceCalendar(models.Model):
    _inherit = 'resource.calendar'

    @api.multi
    def get_working_accurate_hours(self, start_dt=None, end_dt=None):
        """
            Replacement of resource calendar method get_working_hours
            Allows to handle hour_to = 00:00
            Takes in account minutes
            Adds public holidays time (resource leaves with reason = PH)
        """
        leave_obj = self.env['resource.calendar.leaves']
        for calendar in self:
            id = calendar.id
            hours = timedelta()
            for day in rrule.rrule(rrule.DAILY, dtstart=start_dt,
                                   until=end_dt.replace(hour=23, minute=59, second=59),
                                   byweekday=calendar.get_weekdays()[0]):
                day_start_dt = day.replace(hour=0, minute=0, second=0)
                if start_dt and day.date() == start_dt.date():
                    day_start_dt = start_dt
                day_end_dt = day.replace(hour=23, minute=59, second=59)
                if end_dt and day.date() == end_dt.date():
                    day_end_dt = end_dt
                work_limits = []
                work_limits.append((day_start_dt.replace(hour=0, minute=0, second=0), day_start_dt))
                work_limits.append((day_end_dt, day_end_dt.replace(hour=23, minute=59, second=59)))

                intervals = []
                work_dt = day_start_dt.replace(hour=0, minute=0, second=0)
                working_intervals = []
                for calendar_working_day in calendar.get_attendances_for_weekdays([day_start_dt.weekday()])[0]:
                    min_from = int((calendar_working_day.hour_from - int(calendar_working_day.hour_from)) * 60)
                    min_to = int((calendar_working_day.hour_to - int(calendar_working_day.hour_to)) * 60)
                    x = work_dt.replace(hour=int(calendar_working_day.hour_from), minute=min_from)
                    if calendar_working_day.hour_to == 0:
                        y = work_dt.replace(hour=0, minute=0) + timedelta(days=1)
                    else:
                        y = work_dt.replace(hour=int(calendar_working_day.hour_to), minute=min_to)
                    working_interval = (x, y)
                    working_intervals += calendar.interval_remove_leaves(working_interval, work_limits)
                for interval in working_intervals:
                    hours += interval[1] - interval[0]

            # Add public holidays
            leaves = leave_obj.search([('name', '=', 'PH'), ('calendar_id', '=', calendar.id)])
            leave_intervals = []
            for l in leaves:
                leave_intervals.append((datetime.strptime(l.date_from, DTF),
                                        datetime.strptime(l.date_to, DTF)
                                        ))
            clean_intervals = calendar.interval_remove_leaves((start_dt, end_dt), leave_intervals)

            for interval in clean_intervals:
                hours += (end_dt - start_dt) - (interval[1] - interval[0])

        return seconds(hours) / 3600.0

    @api.multi
    def validate_time_limits(self, booking_start, booking_end):
        # localize UTC dates to be able to compare with hours in Working Time
        tz_offset = self.env.context.get('tz_offset')
        if tz_offset:
            start_dt = datetime.strptime(booking_start, DTF) - timedelta(minutes=tz_offset)
            end_dt = datetime.strptime(booking_end, DTF) - timedelta(minutes=tz_offset)
        else:
            user_tz = pytz.timezone(self.env.context.get('tz') or 'UTC')
            start_dt = pytz.utc.localize(fields.Datetime.from_string(booking_start)).astimezone(user_tz)
            end_dt = pytz.utc.localize(fields.Datetime.from_string(booking_end)).astimezone(user_tz)
        for calendar in self:
            hours = calendar.get_working_accurate_hours(start_dt, end_dt)
            duration = seconds(end_dt - start_dt) / 3600.0
            if round(hours, 2) != round(duration, 2):
                return False
        return True

class SaleOrderLine(models.Model):
    _inherit = 'sale.order.line'

    resource_id = fields.Many2one('resource.resource', 'Resource')
    booking_start = fields.Datetime(string="Date start")
    booking_end = fields.Datetime(string="Date end")
    calendar_id = fields.Many2one('resource.calendar', related='product_id.calendar_id')
    project_id = fields.Many2one('account.analytic.account', compute='_compute_dependent_fields', store=False, string='Contract')
    partner_id = fields.Many2one('res.partner', compute='_compute_dependent_fields', store=False, string='Customer')
    overlap = fields.Boolean(compute='_compute_date_overlap', default=False, store=True)
    automatic = fields.Boolean(default=False, store=True, help='automatically generated booking lines')
    active = fields.Boolean(default=True)
    resource_trigger = fields.Integer(help='''we use this feild in _compute_date_overlap instead of resource_id
    because resource_id is related to pitch in pitch_booking module. If we hadn't done it then _compute_date_overlap would be called
    for each line with the same resource instead of only only for current new line''')

    @api.one
    def write(self, vals):
        result = super(SaleOrderLine, self).write(vals)
        if vals.get('resource_id'):
            vals['resource_trigger'] = vals.get('resource_id')
        return result

    @api.multi
    def _compute_dependent_fields(self):
        for line in self:
            line.partner_id = line.order_id.partner_id
            line.project_id = line.order_id.project_id

    @api.multi
    @api.depends('resource_trigger', 'booking_start', 'booking_end', 'active')
    def _compute_date_overlap(self):
        for line in self:
            if not line.active:
                line.overlap = False
                continue
            overlaps = 0
            if line.resource_id and line.booking_start and line.booking_end:
                ids = getattr(self, '_origin', False) and self._origin.ids or bool(line.id) and [line.id] or []
                overlaps = line.search_count([('active', '=', True),
                                              '&', '|', '&', ('booking_start', '>=', line.booking_start), ('booking_start', '<', line.booking_end),
                                              '&', ('booking_end', '>', line.booking_start), ('booking_end', '<=', line.booking_end),
                                              ('resource_id', '!=', False),
                                              ('id', 'not in', ids),
                                              ('resource_id', '=', line.resource_id.id),
                                              ('state', '!=', 'cancel')])
                overlaps += line.search_count([('active', '=', True),
                                               ('id', 'not in', ids),
                                               ('booking_start', '=', line.booking_start),
                                               ('booking_end', '=', line.booking_end),
                                               ('resource_id', '=', line.resource_id.id),
                                               ('state', '!=', 'cancel')])
            line.overlap = bool(overlaps)

    @api.multi
    @api.constrains('overlap')
    def _check_overlap(self):
        for line in self:
            if line.overlap:
                overlaps_with = self.search([('active', '=', True),
                                             '&', '|', '&', ('booking_start', '>', line.booking_start), ('booking_start', '<', line.booking_end),
                                             '&', ('booking_end', '>', line.booking_start), ('booking_end', '<', line.booking_end),
                                             ('resource_id', '!=', False),
                                             ('id', '!=', line.id),
                                             ('resource_id', '=', line.resource_id.id),
                                             ('state', '!=', 'cancel')])
                overlaps_with += self.search([('active', '=', True),
                                              ('id', '!=', line.id),
                                              ('booking_start', '=', line.booking_start),
                                              ('booking_end', '=', line.booking_end),
                                              ('resource_id', '=', line.resource_id.id),
                                              ('state', '!=', 'cancel')])

                msg = 'There are bookings with overlapping times: %(this)s and %(those)s' % {'this': [line.id], 'those': overlaps_with.ids}
                raise ValidationError(msg)

    @api.multi
    @api.constrains('calendar_id', 'booking_start', 'booking_end')
    def _check_date_fit_product_calendar(self):
        for line in self.sudo():
            if line.state == 'cancel':
                continue
            if line.calendar_id and line.booking_start and line.booking_end:
                if not line.calendar_id.validate_time_limits(line.booking_start, line.booking_end):
                    raise ValidationError(_('Not valid interval of booking for the product %s.') % line.product_id.name)

    @api.multi
    @api.constrains('resource_trigger', 'booking_start', 'booking_end')
    def _check_date_fit_resource_calendar(self):
        for line in self.sudo():
            if line.state == 'cancel':
                continue
            if line.resource_id and line.resource_id.calendar_id and line.booking_start and line.booking_end:
                if not line.resource_id.calendar_id.validate_time_limits(line.booking_start, line.booking_end):
                    raise ValidationError(_('Not valid interval of booking for the resource %s.') % line.resource_id.name)

    @api.multi
    @api.constrains('booking_start')
    def _check_booking_start(self):
        for line in self:
            if not line.booking_start:
                continue
            if datetime.strptime(line.booking_start, DTF) + timedelta(minutes=SLOT_START_DELAY_MINS) < datetime.now():
                raise ValidationError(_('Please book on time in %s minutes from now.') % SLOT_START_DELAY_MINS)

    @api.model
    def search_booking_lines(self, start, end, domain):
        domain.append(('state', '!=', 'cancel'))
        domain.append(('booking_end', '>=', fields.Datetime.now()))
        domain.append('|')
        domain.append('|')
        domain.append('&')
        domain.append(('booking_start', '>=', start))
        domain.append(('booking_start', '<', end))
        domain.append('&')
        domain.append(('booking_end', '>', start))
        domain.append(('booking_end', '<=', end))
        domain.append('&')
        domain.append(('booking_start', '<=', start))
        domain.append(('booking_end', '>=', end))
        return self.search(domain)

    @api.model
    def get_bookings(self, start, end, offset, domain):
        bookings = self.sudo().search_booking_lines(start, end, domain)
        return [{
            'className': 'booked_slot resource_%s' % b.resource_id.id,
            'id': b.id,
            'title': b.resource_id.name,
            'start': '%s+00:00' % b.booking_start,
            'end': '%s+00:00' % b.booking_end,
            'resource_id': b.resource_id.id,
            'editable': False,
            'color': b.resource_id.color
        } for b in bookings]

    @api.onchange('booking_start', 'booking_end')
    def _on_change_booking_time(self):
        domain = {'product_id': []}
        if self.venue_id:
            domain['product_id'].append(('venue_id', '=', self.venue_id.id))
        if self.booking_start and self.booking_end:
            start = datetime.strptime(self.booking_start, DTF)
            end = datetime.strptime(self.booking_end, DTF)
            self.product_uom_qty = (end - start).seconds / 3600
            booking_products = self.env['product.product'].search([('calendar_id', '!=', False)])
            domain_products = []
            domain_products = [p.id for p in booking_products
                               if p.calendar_id.validate_time_limits(self.booking_start, self.booking_end)]
            if domain_products:
                domain['product_id'].append(('id', 'in', domain_products))
        return {'domain': domain}

    @api.onchange('partner_id', 'project_id')
    def _on_change_partner(self):
        if self.order_id and self.order_id.partner_id != self.partner_id:
            self.order_id = None
        if self.order_id and self.order_id.project_id != self.project_id:
            self.order_id = None
        return self.env['sale.order'].onchange_partner_id(self.partner_id.id)

    @api.onchange('order_id')
    def _on_change_order(self):
        if self.order_id:
            self.partner_id = self.order_id.partner_id
            self.project_id = self.order_id.project_id

    @api.model
    def read_color(self, color_field):
        return self.env['resource.resource'].browse(color_field).color

    @api.model
    def create(self, values):
        if not values.get('order_id') and values.get('partner_id'):
            order_obj = self.env['sale.order']
            order_vals = order_obj.onchange_partner_id(values.get('partner_id'))['value']
            order_vals.update({
                'partner_id': values.get('partner_id'),
                'project_id': values.get('project_id')
            })
            order = order_obj.create(order_vals)
            values.update({'order_id': order.id})
        return super(SaleOrderLine, self).create(values)

    @api.onchange('product_id')
    def _on_change_product_id(self):
        if self.product_id:
            name = self.product_id.name_get()[0][1]
            if self.product_id.description_sale:
                name += '\n' + self.product_id.description_sale
            self.name = name
            warning = {}
            if self.product_id.sale_line_warn != 'no-message':
                title = _("Warning for %s") % self.product_id.name
                message = self.product_id.sale_line_warn_msg
                warning['title'] = title
                warning['message'] = message
                if self.product_id.sale_line_warn == 'block':
                    return {'value': {'product_id': False}, 'warning': warning}
                else:
                    return {'warning': warning}

    @api.onchange('product_id', 'partner_id', 'product_uom_qty')
    def _on_change_product_partner_id(self):
        if self.product_id and self.partner_id:
            pricelist = self.partner_id.property_product_pricelist
            if pricelist:
                data = self.product_id_change(pricelist.id, self.product_id.id,
                                              qty=self.product_uom_qty, partner_id=self.partner_id.id)
                for k in data['value']:
                    if not k in ['name']:
                        setattr(self, k, data['value'][k])

    @api.model
    def get_resources(self, venue_id, pitch_id):
        pitch_obj = self.env['pitch_booking.pitch'].sudo()
        venue_obj = self.env['pitch_booking.venue'].sudo()
        if not venue_id:
            venues = venue_obj.search([])
            venue_id = venues[0].id if venues else None
        resources = []
        if pitch_id:
            resources = [pitch_obj.browse(int(pitch_id)).resource_id]
        elif venue_id:
            resources = [p.resource_id for p in pitch_obj.search([('venue_id', '=', int(venue_id))])]
        return [{
            'name': r.name,
            'id': r.id,
            'color': r.color
        } for r in resources]

    @api.model
    def generate_slot(self, r, start_dt, end_dt):
        return {
            'start': start_dt.strftime(DTF),
            'end': end_dt.strftime(DTF),
            'title': r.name,
            'color': r.color,
            'className': 'free_slot resource_%s' % r.id,
            'editable': False,
            'resource_id': r.id
        }

    @api.model
    def del_booked_slots(self, slots, start, end, resources, offset, fixed_start_dt, end_dt):
        now = datetime.now() - timedelta(minutes=SLOT_START_DELAY_MINS) - timedelta(minutes=offset)
        lines = self.search_booking_lines(start, end, [('resource_id', 'in', [r['id'] for r in resources])])
        for l in lines:
            line_start_dt = datetime.strptime(l.booking_start, '%Y-%m-%d %H:%M:00') - timedelta(minutes=offset)
            line_start_dt -= timedelta(minutes=divmod(line_start_dt.minute, SLOT_DURATION_MINS)[1])
            line_end_dt = datetime.strptime(l.booking_end, DTF) - timedelta(minutes=offset)
            while line_start_dt < line_end_dt:
                if line_start_dt >= end_dt:
                    break
                elif line_start_dt < fixed_start_dt or line_start_dt < now:
                    line_start_dt += timedelta(minutes=SLOT_DURATION_MINS)
                    continue
                try:
                    del slots[l.resource_id.id][line_start_dt.strftime(DTF)]
                except:
                    _logger.warning('cannot free slot %s %s' % (
                        l.resource_id.id,
                        line_start_dt.strftime(DTF)
                    ))
                line_start_dt += timedelta(minutes=SLOT_DURATION_MINS)
        return slots

    @api.model
    def get_free_slots_resources(self, domain):
        resources = self.env['resource.resource'].search([('to_calendar', '=', True)])
        return resources

    @api.model
    def get_free_slots(self, start, end, offset, domain):
        leave_obj = self.env['resource.calendar.leaves']
        start_dt = datetime.strptime(start, DTF) - timedelta(minutes=offset)
        end_dt = datetime.strptime(end, DTF) - timedelta(minutes=offset)
        fixed_start_dt = start_dt
        resources = self.get_free_slots_resources(domain)
        slots = {}
        now = datetime.now() - timedelta(minutes=SLOT_START_DELAY_MINS) - timedelta(minutes=offset)
        for r in resources:
            if r.id not in slots:
                slots[r.id] = {}
            start_dt = fixed_start_dt
            while start_dt < end_dt:
                if start_dt < now:
                    start_dt += timedelta(minutes=SLOT_DURATION_MINS)
                    continue
                if r.calendar_id:
                    for calendar_working_day in r.calendar_id.get_attendances_for_weekdays([start_dt.weekday()])[0]:
                        min_from = int((calendar_working_day.hour_from - int(calendar_working_day.hour_from)) * 60)
                        min_to = int((calendar_working_day.hour_to - int(calendar_working_day.hour_to)) * 60)
                        x = start_dt.replace(hour=int(calendar_working_day.hour_from), minute=min_from)
                        if calendar_working_day.hour_to == 0:
                            y = start_dt.replace(hour=0, minute=0) + timedelta(days=1)
                        else:
                            y = start_dt.replace(hour=int(calendar_working_day.hour_to), minute=min_to)
                        if r.has_slot_calendar and x >= now and x >= start_dt and y <= end_dt:
                            slots[r.id][x.strftime(DTF)] = self.generate_slot(r, x, y)
                        elif not r.has_slot_calendar:
                            while x < y:
                                if x >= now:
                                    slots[r.id][x.strftime(DTF)] = self.generate_slot(r, x, x + timedelta(minutes=SLOT_DURATION_MINS))
                                x += timedelta(minutes=SLOT_DURATION_MINS)
                    start_dt += timedelta(days=1)
                    start_dt = start_dt.replace(hour=0, minute=0, second=0)
                else:
                    slots[r.id][start_dt.strftime(DTF)] = self.generate_slot(r, start_dt, start_dt + timedelta(minutes=SLOT_DURATION_MINS))
                    start_dt += timedelta(minutes=SLOT_DURATION_MINS)
                    continue
            leaves = leave_obj.search([('name', '=', 'PH'), ('calendar_id', '=', r.calendar_id.id)])
            for leave in leaves:
                from_dt = datetime.strptime(leave.date_from, '%Y-%m-%d %H:%M:00') - timedelta(minutes=offset)
                to_dt = datetime.strptime(leave.date_to, '%Y-%m-%d %H:%M:00') - timedelta(minutes=offset)
                if r.has_slot_calendar:
                    if from_dt >= now and from_dt >= start_dt and to_dt <= end_dt:
                        slots[r.id][from_dt.strftime(DTF)] = self.generate_slot(r, from_dt, end_dt)
                    else:
                        continue
                else:
                    from_dt = max(now, from_dt)
                    while from_dt < to_dt:
                        slots[r.id][from_dt.strftime(DTF)] = self.generate_slot(r, from_dt, from_dt + timedelta(minutes=SLOT_DURATION_MINS))
                        from_dt += timedelta(minutes=SLOT_DURATION_MINS)

        res = []
        for slot in self.del_booked_slots(slots, start, end, resources, offset, fixed_start_dt, end_dt).values():
            for pitch in slot.values():
                res.append(pitch)
        return res

    @api.multi
    def action_open_sale_order(self):
        view_id = self.env.ref('sale.view_order_form')
        order_obj = self.env['sale.order'].search([('id', '=', self.order_id.id)])
        return {
            'type': 'ir.actions.act_window',
            'name': 'Booking',
            'view_type': 'form',
            'view_mode': 'form',
            'view_id': view_id.id,
            'res_id': order_obj.id,
            'res_model': 'sale.order',
            'target': 'current',
        }

class ProductTemplate(models.Model):
    _inherit = 'product.template'

    calendar_id = fields.Many2one('resource.calendar', string='Working time')

class SaleOrderAmountTotal(osv.osv):
    _inherit = 'sale.order'

    def _amount_all_wrapper(self, cr, uid, ids, field_name, arg, context=None):
        return super(SaleOrderAmountTotal, self)._amount_all_wrapper(cr, uid, ids, field_name, arg, context=None)

    def _get_order(self, cr, uid, ids, context=None):
        result = {}
        for line in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context):
            result[line.order_id.id] = True
        return result.keys()

    _columns = {
        'amount_total': old_api_fields.function(_amount_all_wrapper, digits_compute=dp.get_precision('Account'), string='Total',
                                                store={
                                                    'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10),
                                                    'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty', 'state'], 10),
        },
            multi='sums', help="The total amount."),
    }

class SaleOrder(models.Model):
    _inherit = 'sale.order'

    @api.multi
    @api.constrains('state')
    def _check_state(self):
        if self.search_count([('state', 'not in', ['draft'])]) and \
           self.env['sale.order.line'].search_count([('order_id', '=', self.id), ('overlap', '=', 'True')]):
            raise ValidationError(_('There are lines with overlap in this order. Please move overlapping lines to another time or resource'))


"""
2017 Day 6
https://adventofcode.com/2017/day/6
"""

from typing import Sequence, Tuple
import aocd  # type: ignore

def read_initial_blocks(text: str) -> Sequence[int]:
    return tuple(int(val) for val in text.split("\t"))

def largest_block(blocks: Sequence[int]) -> int:
    largest_size = max(blocks)
    return next(block for block, size in enumerate(blocks) if size == largest_size)

def redistribute_from_largest(blocks: Sequence[int]) -> Sequence[int]:
    blocks = list(blocks)
    source = largest_block(blocks)
    redist = blocks[source]
    blocks[source] = 0
    bank = source + 1
    while redist > 0:
        blocks[bank % len(blocks)] += 1
        redist -= 1
        bank += 1
    return tuple(blocks)

def redistribution_cycles_until_repeat(blocks: Sequence[int]) -> Tuple[int, int]:
    seen = set()

    cycles1 = 0
    while blocks not in seen:
        seen.add(blocks)
        blocks = redistribute_from_largest(blocks)
        cycles1 += 1

    seeking = blocks
    cycles2 = 0
    while (blocks != seeking) or (cycles2 == 0):
        blocks = redistribute_from_largest(blocks)
        cycles2 += 1

    return cycles1, cycles2

def main() -> None:
    """
    Calculate and output the solutions based on the real puzzle input.
    """
    data = aocd.get_data(year=2017, day=6)

    blocks = read_initial_blocks(data)
    part1, part2 = redistribution_cycles_until_repeat(blocks)

    print(f"Part 1: {part1}")
    print(f"Part 2: {part2}")

if __name__ == "__main__":
    main()


# %%
from pathlib import Path

import pandas as pd

class PathManager:
    project_dir = Path("../")
    test_dir = Path("./tests/")
    label_by_encode = {
        "rle": {
            "HL": ["H1", "L1"],
            "HHL": ["H2", "H2", "L1"],
            "HLL": ["H1", "L2", "L2"],
            "LL": ["L2", "L2"],
            "LHH": ["L1", "H2", "H2"],
            "LLH": ["L2", "L2", "H1"],
        },
        "rle_delta": {
            "HL": ["H1", "dL1"],
            "HHL": ["H2", "H2", "dL1"],
            "HLL": ["H1", "dL1", "L1"],
            "LL": ["L2", "L2"],
            "LHH": ["L1", "dH1", "H1"],
            "LLH": ["L2", "L2", "dH1"],
        },
    }
    # https://www.akenotsuki.com/kyookotoba/accent/bumpu.html
    accept = {"tokyo": ["HL", "LH", "LHH", "HLL", ],
              # HLL を許すとHLLになる。むしろ、話者もそうなのか？
              "kinki": ["HL", "HH", "LH", "LL", "LLH", "HLL", "HHL", ]}

    @classmethod
    def data_path(cls, data_type, wav_path="", is_test=False) -> Path:
        """
        wav_path: wav_path じゃなくても良い(どのみち処理するので)
        """
        # 参照したいタイプを渡すとパスを返す
        if is_test:
            project_dir = cls.test_dir
        else:
            project_dir = cls.project_dir
        # NOTE: 予測に失敗した時、近畿のLHH拒否が原因かもしれない
        accepted_types = [
            "original", "downsample", "feature",
            "label_base", "label_rle",
            "axb",
        ]
        assert data_type in accepted_types
        data_path_map = {
            "original": project_dir / "src/audio/output" / wav_path,
            "downsample": project_dir / "model/wav" / wav_path,
            "feature": project_dir / "model/feature" / str(wav_path.split(".")[0]+".npy"),
            "label_base": project_dir / "model/label_base" / str(wav_path.split(".")[0]+".npy"),
            "label_rle": project_dir / "model/label_rle" / str(wav_path.split(".")[0]+".npy"),
        }
        return data_path_map[data_type]

    @classmethod
    def train_test_wav(cls, is_test=False):
        if is_test:
            tone_df = pd.read_csv(cls.test_dir/"src/list/axb_list.csv")\
                .query("type=='filler'")[["a", "x", "b"]]
        else:
            tone_df = pd.read_csv(cls.project_dir/"src/list/axb_list.csv")\
                .query("type=='filler'")[["a", "x", "b"]]
        wav_list_set = set(
            tone_df.a.values.tolist() +
            tone_df.x.values.tolist() +
            tone_df.b.values.tolist()
        )
        # _を含むのはH_Lのようなテストデータ
        train_wav_list = list(
            filter(lambda s: s.count("_") == 0,
                   wav_list_set)
        )
        test_wav_list = list(filter(lambda s: s.count("_") != 0, wav_list_set))
        return train_wav_list, test_wav_list


import re
from random import choice
from dataclasses import dataclass
from typing import Optional

from .base import RuleItem, RuleDevice, BaseAgent
from magic_agent.core.rules import (MozillaDefault,
                                    SafariMobile,
                                    Chrome,
                                    AppleWebKit,
                                    LikeGecko,
                                    SafariRandom,
                                    Safari,
                                    AppleWebkitRandom)

@dataclass(frozen=True)
class MobileDevice:
    """Base mobile device dataclass"""
    model: str
    released: int
    operating_system: str
    platform: str
    battery_capacity: int
    model_id: Optional[list[str]] = None
    market_regions: Optional[list[str]] = None
    codename: Optional[str] = None
    oem_id: Optional[str] = None
    market_countries: Optional[str] = None
    resolution: Optional[dict] = None

    @classmethod
    def generate_device(cls, **kwargs):
        """generate devices with json data"""
        return cls(**kwargs)

    @property
    def is_android(self) -> bool:
        return "android" in self.platform.lower()

    @property
    def is_apple(self) -> bool:
        return "ios" in self.platform.lower()

    @property
    def version(self) -> str:
        """return operating system version"""
        if self.is_android:
            return re.findall(r'Android (.*?) ', self.operating_system)[0]
        elif self.is_apple:
            return re.findall(r' iOS (.*?)', self.operating_system)[0]
        return ""

    @property
    def agent(self) -> BaseAgent:
        """return BaseAgent class"""
        return MobileAgent(self).agent()

class MobileAgent:
    """Convert Device class to useragent"""
    __RE_SYSTEM_VERSION = re.compile(r"([\d.]+)")

    def __init__(self, device: MobileDevice):
        self._device = device

    def agent(self, *, by_device_name: bool = True, by_model_id: bool = True) -> BaseAgent:
        """return singe device BaseAgent class"""
        if self._device.is_android:
            return self._android_agent(by_model_id, by_device_name)

        elif self._device.is_apple:
            return self._apple_agent()

    @property
    def _system_version(self) -> str:
        return self.__RE_SYSTEM_VERSION.findall(self._device.operating_system)[0]

    def _android_agent(self, by_model_id, by_device_name):
        device_items = ["Linux"]
        if self._device.operating_system:
            device_items.append(f"Android {self._system_version}")
        else:
            device_items.append("Android 10.0")

        if by_model_id and self._device.model_id:
            device_items.append(choice(self._device.model_id))

        elif by_device_name:
            device_name = " ".join(self._device.model.split(" ")[:3])
            device_items.append(device_name)

        device = RuleDevice(
            items=tuple(device_items))

        return BaseAgent(
            rules=(
                MozillaDefault,
                device,
                AppleWebKit,
                LikeGecko,
                Chrome,
                choice((Safari, SafariMobile))
            ))

    def _apple_agent(self):
        if "iPhone" in self._device.model:
            device_items = ["iPhone"]
        elif "iPad" in self._device.model:
            device_items = ["iPad"]
        elif "iPod" in self._device.model:
            device_items = ["iPod"]
        else:
            raise TypeError("Unknown apple device passed")
        o_sys = self._system_version

        if "." not in o_sys:
            apple_version = RuleItem(f"Version/{o_sys}.0")  # Version string
            o_sys += "_0"
        else:
            apple_version = RuleItem(f"Version/{o_sys}")
            o_sys = o_sys.replace(".", "_")

        device_items.append(f"CPU OS {o_sys} like Mac OS X")
        device = RuleDevice(items=tuple(device_items))
        device_build = RuleItem(f"Mobile/{self._device.model_id[0]}")
        return BaseAgent(
            rules=(
                MozillaDefault,
                device,
                AppleWebkitRandom,
                LikeGecko,
                apple_version,
                device_build,
                SafariRandom
            ))

    def __str__(self):
        return self.agent().agent


from typing import Any, Text, Dict, List, Optional
from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.events import SlotSet
import json
import sys

sys.path.append("utils")
from rocketchat_api_calls import get_user_info

class ActionUserIdentifier(Action):
    """
    Check and store in a slot the user of the conversation.
    """

    def name(self) -> Text:
        return "action_user_identifier"

    async def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[Dict[Text, Any]]:
        if not tracker.slots.get("user"):
            self.tracker = tracker
            user_id = self._digest_sender_id()

            if user_id:
                user_data = get_user_info(user_id).get("user")
                user_name = user_data.get("name")
            else:
                user_data = None
                user_name = "usuario"

            return [SlotSet("user", user_data), SlotSet("user_name", user_name)]

        return []

    def _digest_sender_id(self) -> Optional[Text]:
        try:
            sender_id = self.tracker.sender_id
            if len(sender_id) != 34:
                return None
            sender_id_1 = sender_id[:17]
            sender_id_2 = sender_id[17:]

            try:
                rol = get_user_info(sender_id_1).get("user").get("roles")
                if rol == None:
                    rol = []
            except Exception as e:
                rol = ["other"]

            if "bot" in rol:
                user_id = sender_id_2
            else:
                user_id = sender_id_1
            return user_id
        except Exception as e:
            print(e)
            return None


from typing import List

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
     def generateTrees(self, n: int) -> List[TreeNode]:

        # 核心思想
        # 递归求解
        # 与96题不同，96题无需关注数字，只关心序列长度
        # 本体需要关注序列的起始于终止范围
        # 设定一个helper函数，传入初始start和end值，即[1, n]
        # 依次取[start, end]中的值i作为根节点
        # 则左节点的范围是leftTrees = helper(start, i - 1)
        # 则右节点的范围是rightTrees = helper(i + 1, end)
        # 递归调用这两部分，获得所有可行的左子树和可行的右子树，
        # 那么最后一步我们只要从可行左子树集合中选一棵，再从可行右子树集合中选一棵拼接到根节点上，
        # 并将生成的二叉搜索树放入答案数组即可。

        # 当start > end时，无法满足二插树条件：右子树要比左子树小，则说明无法再进行划分，
        # 注意要返回一个可迭代对象[None]

        def helper(start, end):
            allTrees = []

            if start > end:
                return [None]

            for i in range(start, end + 1):  # 枚举可行根节点
                # 获得所有可行的左子树集合
                leftTrees = helper(start, i - 1)

                # 获得所有可行的右子树集合
                rightTrees = helper(i + 1, end)

                # 从左子树集合中选出一棵左子树，从右子树集合中选出一棵右子树，拼接到根节点上
                for l in leftTrees:
                    for r in rightTrees:
                        currTree = TreeNode(i)
                        currTree.left = l
                        currTree.right = r
                        allTrees.append(currTree)

            return allTrees  # 返回allTrees，就是返回该节点下的所有子树集合

        return helper(1, n) if n else []

95.不同的二叉搜索树II.py

95.不同的二叉搜索树II.py


from graphviz import Digraph
from Instructions import *

index = 0
dot = None

def inc():
    global index
    index += 1
    return index

def GraphAST(AST, consola):
    global dot
    dot = Digraph()
    dot.attr(splines = 'false')
    programIndex = inc()
    dot.node(str(index), 'PROGRAM')
    inc()
    program = AST

    if program.main != None:
        labels = [program.main] + program.labels
        for label in labels:
            AST_Label(programIndex, label)

    try:
        dot.view()
    except:
        consola.setText('ERROR: No fue posible realizar el reporte AST por algun error desconocido.')

def AST_Label(father, label):
    labelIndex = inc()
    dot.node(str(labelIndex), label.name)
    dot.edge(str(father), str(labelIndex))

    for instruction in label.instructions:
        if isinstance(instruction, Unset): AST_Unset(labelIndex, instruction)
        elif isinstance(instruction, Set): AST_Set(labelIndex, instruction)
        elif isinstance(instruction, Goto): AST_Goto(labelIndex, instruction)
        elif isinstance(instruction, If): AST_If(labelIndex, instruction)
        elif isinstance(instruction, Print): AST_Print(labelIndex, instruction)
        elif isinstance(instruction, Exit): AST_Exit(labelIndex, instruction)

def AST_Unset(father, instruction):
    unsetIndex = inc()
    dot.node(str(unsetIndex), 'UNSET')
    dot.edge(str(father), str(unsetIndex))
    if isinstance(instruction.register, Register): AST_Register(unsetIndex, instruction.register)
    elif isinstance(instruction.register, Array): AST_Array(unsetIndex, instruction.register)

def AST_Print(father, instruction):
    printIndex = inc()
    dot.node(str(printIndex), 'PRINT')
    dot.edge(str(father), str(printIndex))
    if isinstance(instruction.register, Register): AST_Register(printIndex, instruction.register)
    elif isinstance(instruction.register, Array): AST_Array(printIndex, instruction.register)

def AST_Goto(father, instruction):
    gotoIndex = inc()
    dot.node(str(gotoIndex), 'GOTO')
    dot.edge(str(father), str(gotoIndex))
    son = inc()
    dot.node(str(son), instruction.label)
    dot.edge(str(gotoIndex), str(son))

def AST_If(father, instruction):
    ifIndex = inc()
    dot.node(str(ifIndex), 'IF')
    dot.edge(str(father), str(ifIndex))

    if isinstance(instruction.condition, Register): AST_Register(ifIndex, instruction.condition)
    elif isinstance(instruction.condition, Array): AST_Array(ifIndex, instruction.condition)
    elif isinstance(instruction.condition, AritmeticOperation): AST_Operation(ifIndex, instruction.condition, 'ARITMETIC')
    elif isinstance(instruction.condition, BitxbitOperation): AST_Operation(ifIndex, instruction.condition, 'BIT X BIT')
    elif isinstance(instruction.condition, LogicOperation): AST_Operation(ifIndex, instruction.condition, 'LOGIC')
    elif isinstance(instruction.condition, RelationalOperation): AST_Operation(ifIndex, instruction.condition, 'RELATIONAL')
    AST_Goto(ifIndex, instruction.goto)

def AST_Set(father, instruction):
    setIndex = inc()
    dot.node(str(setIndex), 'SET')
    dot.edge(str(father), str(setIndex))

    son = inc()
    dot.node(str(son), '=')
    dot.edge(str(setIndex), str(son))

    if isinstance(instruction.register, Register): AST_Register(son, instruction.register)
    elif isinstance(instruction.register, Array): AST_Array(son, instruction.register)

    if isinstance(instruction.data, Register): AST_Register(son, instruction.data)
    elif isinstance(instruction.data, Array): AST_Array(son, instruction.data)
    elif isinstance(instruction.data, AritmeticOperation): AST_Operation(son, instruction.data, 'ARITMETIC')
    elif isinstance(instruction.data, BitxbitOperation): AST_Operation(son, instruction.data, 'BIT X BIT')
    elif isinstance(instruction.data, LogicOperation): AST_Operation(son, instruction.data, 'LOGIC')
    elif isinstance(instruction.data, RelationalOperation): AST_Operation(son, instruction.data, 'RELATIONAL')
    elif isinstance(instruction.data, ArrayDeclaration): AST_ArrayDeclaration(son, instruction.data)
    elif isinstance(instruction.data, Read): AST_Read(son, instruction.data)
    elif isinstance(instruction.data, Cast): AST_Cast(son, instruction.data)
    elif isinstance(instruction.data, Primary): AST_Primary(son, instruction.data)
    elif isinstance(instruction.data, Pointer): AST_Pointer(son, instruction.data)

def AST_Register(father, expression):
    registerIndex = inc()
    dot.node(str(registerIndex), 'REGISTER')
    dot.edge(str(father), str(registerIndex))
    regType = ''
    if expression.type_ == REGISTER.TEMPORAL: regType = 'TEMPORAL'
    elif expression.type_ == REGISTER.PARAM: regType = 'PARAMETRO'
    elif expression.type_ == REGISTER.PILA: regType = 'PILA'
    elif expression.type_ == REGISTER.DEVUELTO: regType = 'RETORNO'
    elif expression.type_ == REGISTER.RETURNED: regType = 'RECURSIVIDAD'
    elif expression.type_ == REGISTER.PUNTERO: regType = 'PUNTERO PILA'
    son = inc()
    dot.node(str(son), regType)
    dot.edge(str(registerIndex), str(son))
    son = inc()
    dot.node(str(son), str(expression.position))
    dot.edge(str(registerIndex), str(son))

def AST_Array(father, expression):
    arrayIndex = inc()
    dot.node(str(arrayIndex), 'ARRAY')
    dot.edge(str(father), str(arrayIndex))
    regType = ''
    if expression.type_ == REGISTER.TEMPORAL: regType = 'TEMPORAL'
    elif expression.type_ == REGISTER.PARAM: regType = 'PARAMETRO'
    elif expression.type_ == REGISTER.PILA: regType = 'PILA'
    elif expression.type_ == REGISTER.DEVUELTO: regType = 'RETORNO'
    elif expression.type_ == REGISTER.RETURNED: regType = 'RECURSIVIDAD'
    elif expression.type_ == REGISTER.PUNTERO: regType = 'PUNTERO PILA'
    son = inc()
    dot.node(str(son), regType)
    dot.edge(str(arrayIndex), str(son))
    son = inc()
    dot.node(str(son), str(expression.position))
    dot.edge(str(arrayIndex), str(son))
    count = 1
    for index_ in expression.indexs:
        AST_Position(arrayIndex, index_, 'INDICE '+ str(count))
        count += 1

def AST_Position(father, expression, count):
    positionIndex = inc()
    dot.node(str(positionIndex), count)
    dot.edge(str(father), str(positionIndex))
    if isinstance(expression.index, Register): AST_Register(positionIndex, expression.index)
    elif isinstance(expression.index, Primary): AST_Primary(positionIndex, expression.index)

def AST_Primary(father, expression):
    primaryIndex = inc()
    dot.node(str(primaryIndex), str(expression.data))
    dot.edge(str(father), str(primaryIndex))

def AST_Read(father, instruction):
    readIndex = inc()
    dot.node(str(readIndex), 'READ')
    dot.edge(str(father), str(readIndex))

def AST_ArrayDeclaration(father, instruction):
    arrayDeclarationIndex = inc()
    dot.node(str(arrayDeclarationIndex), 'ARRAY')
    dot.edge(str(father), str(arrayDeclarationIndex))

def AST_Cast(father, instruction):
    castIndex = inc()
    dot.node(str(castIndex), 'CASTING')
    dot.edge(str(father), str(castIndex))
    cast = ''
    if instruction.casting == DATA_TYPE.CHARACTER: cast = 'CHAR'
    elif instruction.casting == DATA_TYPE.INTEGER: cast = 'INT'
    elif instruction.casting == DATA_TYPE.FLOAT: cast = 'FLOAT'
    son = inc()
    dot.node(str(son), cast)
    dot.edge(str(castIndex), str(son))
    if isinstance(instruction.data, Register): AST_Register(castIndex, instruction.data)
    elif isinstance(instruction.data, Array): AST_Array(castIndex, instruction.data)

def AST_Pointer(father, instruction):
    pointerIndex = inc()
    dot.node(str(pointerIndex), 'POINTER')
    dot.edge(str(father), str(pointerIndex))
    if isinstance(instruction.register, Register): AST_Register(pointerIndex, instruction.register)
    elif isinstance(instruction.register, Array): AST_Array(pointerIndex, instruction.register)

def AST_Exit(father, instruction):
    exitIndex = inc()
    dot.node(str(exitIndex), 'EXIT')
    dot.edge(str(father), str(exitIndex))

def AST_Operation(father, expression, tipo):
    operationIndex = inc()
    dot.node(str(operationIndex), tipo)
    dot.edge(str(father), str(operationIndex))
    simbol = ''
    if expression.operator == ARITMETIC_OPERATION.NEG: simbol = '-'
    elif expression.operator == ARITMETIC_OPERATION.SUMA: simbol = '+'
    elif expression.operator == ARITMETIC_OPERATION.RESTA: simbol = '-'
    elif expression.operator == ARITMETIC_OPERATION.MULT: simbol = '*'
    elif expression.operator == ARITMETIC_OPERATION.DIV: simbol = '/'
    elif expression.operator == ARITMETIC_OPERATION.MOD: simbol = '%'
    elif expression.operator == ARITMETIC_OPERATION.ABS: simbol = 'ABSOLUT'
    elif expression.operator == LOGIC_OPERATION.AND: simbol = '&&'
    elif expression.operator == LOGIC_OPERATION.OR: simbol = '||'
    elif expression.operator == LOGIC_OPERATION.NOT: simbol = '!'
    elif expression.operator == RELATIONAL_OPERATION.ES_IGUAL: simbol = '=='
    elif expression.operator == RELATIONAL_OPERATION.NO_IGUAL: simbol = '!='
    elif expression.operator == RELATIONAL_OPERATION.MAYOR: simbol = '>'
    elif expression.operator == RELATIONAL_OPERATION.MENOR: simbol = '<'
    elif expression.operator == RELATIONAL_OPERATION.MAYOR_IGUAL: simbol = '>='
    elif expression.operator == RELATIONAL_OPERATION.MENOR_IGUAL: simbol = '<='

    son = inc()
    dot.node(str(son), simbol)
    dot.edge(str(operationIndex), str(son))

    if isinstance(expression.expression1, Register): AST_Register(son, expression.expression1)
    elif isinstance(expression.expression1, Array): AST_Array(son, expression.expression1)
    elif isinstance(expression.expression1, Primary): AST_Primary(son, expression.expression1)

    if isinstance(expression.expression2, Register): AST_Register(son, expression.expression2)
    elif isinstance(expression.expression2, Array): AST_Array(son, expression.expression2)
    elif isinstance(expression.expression2, Primary): AST_Primary(son, expression.expression2)


import pexpect
import os
from glob import glob
from os import stat as os_stat
from pp_utils import Monitor
from stat import S_ISFIFO

"""
    based on pyomxplayer from https://github.com/jbaiter/pyomxplayer
    extensively modified by KenT

    uzblDriver hides the detail of using the luakit command  from browserplayer
    This is meant to be used with pp_browserplayer.py
    Its easy to end up with many copies of uzbl running if this class is not used with care.
    use pp_browserplayer.py for a safer interface.

    External commands
    ----------------------------
    __init__ just creates the instance and initialises variables (e.g. bplayer=uzblDriver())
    play -  opens the browser and plays the first track
    control  - sends commands to uzbl while it is open
    stop - closes the browser.
    terminate - Stops the browser. Used when aborting an application.

    Signals
    ----------
    The following signals are produced while the browser is open
         self.start_play_signal = True when the browser is ready to be used

    is_running() tests whether the sub-process running uzbl is alive.

"""

class UZBLDriver(object):

    def __init__(self,widget):

        self.widget=widget

        self.mon=Monitor()

        self._process=None
        self.fifo=''

    def pause(self):
        pass

    def stop(self):
        self.control('exit')

    # kill the subprocess (uzbl). Used for tidy up on exit.
    def terminate(self,reason):
        self.terminate_reason=reason
        if self.exists_fifo():
            self.control('exit')
            # self._process.close(force=True)

    def play(self, track, geometry):
        self.start_play_signal = False
        # track= "'"+ track.replace("'","'\\''") + "'"

        cmd='uzbl-browser '  + geometry + '--uri='+track
        self.mon.log(self, "Send command to uzbl: "+ cmd)
        self._process = pexpect.spawn(cmd)
        # uncomment to monitor output to and input from uzbl (read pexpect manual)
        # fout= file('/home/pi/pipresents/uzbllogfile.txt','w')  #uncomment and change sys.stdout to fout to log to a file
        # self._process.logfile_send = sys.stdout  # send just commands to stdout
        # self._process.logfile=fout  # send all communications to log

        # and poll for fifo to be available
        self.get_fifo()

    # poll for fifo to be available
    # when it is set start_play_signal
    # browser player calls is_running at intervals to test if uzbl is ended after an exit command
    def get_fifo(self):
        """
        Look for UZBL's FIFO-file in /tmp.
        Don't give up until it has been found.
        """
        candidates = glob('/tmp/uzbl_fifo_*')
        for fifo_file in candidates:
            if S_ISFIFO(os_stat(fifo_file).st_mode):
                self.mon.log(self, 'Found UZBL fifo  in %s.' % fifo_file)
                self.fifo=fifo_file
                self.start_play_signal=True
                return
        # print 'fifo not found trying again'
        self.widget.after(500,self.get_fifo)

    def exists_fifo(self):
        if os.path.exists(self.fifo):
            return True
        else:
            return False

    # send commands to uzbl via the fifo
    def control(self,data):
        if self.exists_fifo():
            self.mon.log(self,'send command to uzbl:'+ data)
            f = open(self.fifo, 'a')
            f.write('%s\n' % data)
            f.close()
            # self.mon.log(self,'sent command to uzbl:'+ data)

   # test of whether _process is running
    def is_running(self):
        return self._process.isalive()


# solved: [1926] 그림
# https://www.acmicpc.net/problem/1926
# graph-traversal, bfs
#
# Silver 1
# bfs 기본 문제
# 주어진 그래프에서 1의 영역의 개수, 최대 영역의 크기

from collections import deque

def bfs(x, y):

    queue = deque()
    queue.append((x, y))
    area = 1

    while queue:

        x, y = queue.popleft()

        for i in range(4):

            nx = x + dx[i]
            ny = y + dy[i]

            if nx < 0 or ny < 0 or nx >= n or ny >= m:
                continue

            if visited[nx][ny]:
                continue

            if graph[nx][ny]:
                visited[nx][ny] = True
                area += 1
                queue.append((nx, ny))

    return area

n, m = map(int, input().split())
graph = [list(map(int, input().split())) for _ in range(n)]

visited = [[False] * m for _ in range(n)]
dx = [1, 0, -1, 0]
dy = [0, 1, 0, -1]
cnt = 0
ans = 0

for i in range(n):
    for j in range(m):
        if graph[i][j] and not visited[i][j]:
            visited[i][j] = True
            cnt += 1
            ans = max(ans, bfs(i, j))

print(cnt)
print(ans)


import PIL
from torchvision import transforms
import time
import torch
import torch.nn as nn
import math
import numpy as np
import torch

steps =5

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.cnn = nn.Sequential(nn.Conv2d(3,5,kernel_size=3),
                                             nn.Conv2d(5, 5, kernel_size=2,stride=2),
                                             nn.MaxPool2d(kernel_size=2),
                                             nn.Dropout(0.3),
                                             nn.Conv2d(5,12,kernel_size=2),
                                             nn.Conv2d(12, 12, kernel_size=2, stride=2),
                                             nn.MaxPool2d(kernel_size=2),
                                             nn.Dropout(0.2),
                                             nn.Conv2d(12,24,kernel_size=2),
                                             nn.Conv2d(24, 24, kernel_size=2, stride=2),
                                             nn.MaxPool2d(kernel_size=2),
                                             nn.Dropout(0.2),
                                             nn.Conv2d(24, 30, kernel_size=2),
                                             nn.Flatten(),
                                             nn.GELU(),
                                        nn.Linear(120,32),
                                        nn.GELU(),
                                        nn.Linear(32, 2),
                                             nn.Softmax(dim=1))

    def forward(self, input):
        return self.cnn(input)

def Train( device='cuda'):
    trans = transforms.Compose([transforms.ToTensor(),
                                transforms.RandomHorizontalFlip(0.5),
                                transforms.Normalize(mean=(0.5,0.5,0.5),std=(0.5,0.5,0.5))])
    criterion = nn.CrossEntropyLoss()
    model = CNN().to(device)
    optimizer = torch.optim.RAdam(model.parameters(),lr=5e-4)
    for _ in range(steps):
        loss_average = 0
        count =0
        FaceAdder, HelmetAdder = 1, 1
        correctness=0
        for i in range(638):
            try:
                count+=1
                if i % 2 == 0:
                    url = './helmet/Helmet_' + str(HelmetAdder) + '.jpg'
                    HelmetAdder += 1
                    Answer = torch.tensor([0]).to(device)
                else:
                    url = './Face/Face_' + str(FaceAdder) + '.jpg'
                    FaceAdder += 1
                    Answer = torch.tensor([1]).to(device)
                Img = PIL.Image.open(url)
                Img = Img.resize((256, 256))
                Img = Img.convert("RGB")
                Img = trans(Img).float().to(device)
                predict = model(Img.reshape(-1, 3, 256, 256))
                loss = criterion(predict, Answer)
                optimizer.zero_grad()
                loss.backward()
                loss_average += loss.item()
                optimizer.step()
                if torch.argmax(predict, dim=1).item() == Answer:
                    correctness += 1
            except:
                pass
        torch.save(model.state_dict(),'./saved.pt')
        print(loss_average / count,correctness/count*100)

def Value( device='cuda'):
    trans = transforms.Compose([transforms.ToTensor(),
                                transforms.RandomHorizontalFlip(0.5),
                                transforms.Normalize(mean=(0.5,0.5,0.5),std=(0.5,0.5,0.5))])
    model = CNN().to(device)
    saved = torch.load('./saved.pt')
    model.load_state_dict(saved)
    model.eval()
    count=0
    FaceAdder, HelmetAdder = 1, 1
    correctness = 0
    for i in range(638):
        try:
            if i % 2 == 0:
                url = './helmet/Helmet_' + str(HelmetAdder) + '.jpg'
                HelmetAdder += 1
                Answer = torch.zeros(1, 2).to(device)
                Answer[0][0] += 1
            else:
                url = './Face/Face_' + str(FaceAdder) + '.jpg'
                FaceAdder += 1
                Answer = torch.zeros(1, 2).to(device)
                Answer[0][1] += 1

            Img = PIL.Image.open(url)

            Img = Img.resize((256, 256))
            Img = Img.convert("RGB")
            Img = trans(Img).float().to(device)
            predict = model(Img.reshape(-1, 3, 256, 256))
            count +=1
            if torch.argmax(predict,dim=1).item() == torch.argmax(Answer,dim=1).item():
                correctness+=1
        except:
            pass
    print(correctness/count*100,"%")

if __name__ == '__main__':
    Train()
    Value()


"""Phrase search by dumb numpy position differences subtraction, bigram by bigram."""
import numpy as np
from typing import List

def vstack_with_mask(posns: List[np.ndarray], phrase_freqs: np.ndarray, width: int = 0, pad: int = -100):
    """Vertically stack arrays, only accepting those that fit within width.

    Parameters
    ----------
    posns: list of np.ndarray term positions for a given term across multiple docs
    width: int, max number of positions we accept
    pad: int, value to pad with when no position present
    phrase_freqs: np.ndarray, phrase freqs for each doc.
                 -1 indicates "not yet processed" 0 means "defiiitely not a match"

    Returns
    -------
    vstacked: np.ndarray, vertically stacked arrays of those <= width
    mask: np.ndarray, boolean mask of which arrays were accepted (so you can recompute them a different way)
    """
    vstacked = np.zeros((len(posns), width), dtype=posns[0].dtype) + pad
    if not isinstance(posns[0], np.ndarray):
        raise TypeError("posns must be a list of np.ndarrays")
    for idx, array in enumerate(posns):
        if len(array) >= width:
            phrase_freqs[idx] = -2  # Mark as skip this round
        else:
            vstacked[idx, :len(array)] = array
    return vstacked

def stack_term_posns(term_posns: List[np.ndarray], phrase_freqs: np.ndarray, width: int = 10):
    # Pad for easy difference computation
    keep_term_posns = []
    # keep_mask = np.ones(len(self), dtype=bool)
    for term_posn in term_posns:
        this_term_posns = vstack_with_mask(term_posn, phrase_freqs, width=width)
        keep_term_posns.append(this_term_posns)
    return keep_term_posns

def _compute_phrase_freqs(term_posns, phrase_freqs: np.ndarray, slop=1):

    to_compute = phrase_freqs == -1

    if not np.any(to_compute):
        return phrase_freqs

    # Only examine masked
    term_posns = [term_posn[to_compute] for term_posn in term_posns]

    prior_term = term_posns[0]
    for term in term_posns[1:]:
        is_same_term = (term.shape == prior_term.shape) and np.all(term == prior_term)

        # Compute positional differences
        #
        # Each row of posn_diffs is a term posn diff matrix
        # Where columns are prior_term posns, rows are term posns
        # This shows every possible term diff
        #
        # Example:
        #   prior_term = array([[0, 4],[0, 4])
        #         term = array([[1, 2, 3],[1, 2, 3]])
        #
        #
        #   posn_diffs =
        #
        #     array([[ term[0] - prior_term[0], term[0] - prior_term[1] ],
        #            [ term[1] - prior_term[0], ...
        #            [ term[2] - prior_term[0], ...
        #
        #    or in our example
        #
        #     array([[ 1, -3],
        #            [ 2, -2],
        #            [ 3, -1]])
        #
        #  We care about all locations where posn == slop (or perhaps <= slop)
        #  that is term is slop away from prior_term. Usually slop == 1 (ie 1 posn away)
        #  for normal phrase matching
        #
        posn_diffs = term[:, :, np.newaxis] - prior_term[:, np.newaxis, :]

        # For > 2 terms, we need to connect a third term by making prior_term = term
        # and repeating
        #
        # BUT
        # we only want those parts of term that are adjacent to prior_term
        # before continuing, so we don't accidentally get a partial phrase
        # so we need to make sure to
        # Pad out any rows in 'term' where posn diff != slop
        # so they're not considered on subsequent iterations
        term_mask = np.any(posn_diffs == 1, axis=2)
        term[~term_mask] = -100

        # Count how many times the row term is 1 away from the col term
        per_doc_diffs = np.sum(posn_diffs == slop, axis=1, dtype=np.int8)

        # Doc-wise sum to get a 'term freq' for the prior_term - term bigram
        bigram_freqs = np.sum(per_doc_diffs == slop, axis=1)
        if is_same_term:
            satisfies_slop = per_doc_diffs == slop
            consecutive_ones = satisfies_slop[:, 1:] & satisfies_slop[:, :-1]
            consecutive_ones = np.sum(consecutive_ones, axis=1)
            # ceiling divide?
            # Really these show up as
            # 1 1 1 0 1
            # we need to treat the 2nd consecutive 1 as 'not a match'
            # and also update 'term' to not include it
            bigram_freqs -= -np.floor_divide(consecutive_ones, -2)

        # Last loop, bigram_freqs is the full phrase term freq

        # Update mask to eliminate any non-matches
        phrase_freqs[to_compute] = bigram_freqs

        # Should only keep positions of 'prior term' that are adjacent to the
        # one prior to it...
        prior_term = term

    return phrase_freqs

def compute_phrase_freqs(term_posns, phrase_freqs, slop=1, width=10):
    """Compute phrase freq using matrix-diff method for docs up to width posns. Skip others.

    Parameters
    ----------
    term_posns: list of np.ndarray term positions for a given term across multiple docs
    phrase_freqs: np.ndarray, phrase freqs for each doc present in term_posns

    Returns
    -------
    phrase_freqs: np.ndarray, phrase freqs for each doc present in mask

    See Also
    --------
    Colab notebook: https://colab.research.google.com/drive/1NRxeO8Ya8jSlFP5YwZaGh1-43kDH4OXG?authuser=1#scrollTo=5JZV8svpauYB
    """
    if len(term_posns[0]) != len(phrase_freqs):
        raise ValueError("term_posns and phrase_freqs must be same length")
    stacked = stack_term_posns(term_posns, phrase_freqs, width=width)
    phrase_freqs = _compute_phrase_freqs(stacked, phrase_freqs, slop=slop)
    phrase_freqs[phrase_freqs == -2] = -1
    return phrase_freqs