# this project is licensed under the WTFPLv2, see COPYING.txt for details

"""Color scheme application

This plugin allows to load color scheme definition from a file and apply them to editor and syntax coloring.

Color scheme format
===================

Color scheme files are in INI format (as read by `configparser`). A scheme file sets style attributes for multiples
lexers and such descriptions can be applied to an editor.

Sections
--------

A color scheme file consist in one or more INI sections, each corresponding to a lexer.
The special section `*` applies to all lexers. When applying a color scheme to an editor, the `*` section is applied
first if it exists, then the section for the lexer of the editor is applied, if it exists and if the editor has
a lexer.

Attributes
----------

Within a section, there are multiple entries in the form: `item_kind.item_name.style_property = style_value`.

The `item_kind.item_name` part specifies for which items of the lexer it should apply, for example:
the "keyword" token, the "search" indicator, or simply the caret style.

The `style_property` indicates what attribute should be set, for example: the font size, the background color.

Finally, the `style_value` is the value the `style_property` should be set to, for example: a color, a size, a
font name.

Item types
----------

Editor properties
+++++++++++++++++

If `item_kind` is `base`, `item_name` can be:

* `text`: applies to normal text
* `selection`': applies to selected text
* `whitespace`
* `caret`: applies to caret, for `foreground`, or whole line under cursor, for `background` (if set visible)
* `hotspot`
* `matchedbrace`: for a brace under cursor, which has a corresponding matching brace
* `unmatchedbrace`: for a brace under cursor, which doesn't have a corresponding matching brace
* `margin`: for the margin column

The valid `style_property` are `foreground`, `background`, `font`, `points`, `bold`, `italic`, `underline`.

Tokens
++++++

If `item_kind` is `token`, `item_name` is the token name, which corresponds to one style in `QsciLexer`.

The token name `*` is special: it matches all tokens, and is applied first, so it can be overwritten by more
specific color scheme entries.

The valid `style_property` are `foreground`, `background`, `font`, `points`, `bold`, `italic`, `underline`.

Styles
++++++

If `item_kind` is `style`, `item_name` is the style name, as registered in :any:`eye.helpers.styles`.

The valid `style_property` are `foreground`, `background`, `font`, `points`, `bold`, `italic`, `underline` and
`eolfill`.

Indicators
++++++++++

If `item_kind` is `indicator`, `item_name` is the indicator name (see :any:`eye.widgets.editor.Editor.indicators`)

The valid `style_property` are: `foreground`, `background` and `style`.

Property types
--------------

* `foreground` (or `fg`, or `color`) specifies the text color
* `background` (or `bg`) specifies the background color

* `font` specifies the font family (font name)
* `points` specifies the font size in points

* `bold` specifies whether the font is bold
* `italic` specifies whether the font is italic
* `underline` specifies whether the font is underline

* `eolfill` specifies whether the background color applies to the rest of the line, after last character

* `style` specifies which indicator style should be used, e.g. `StraightBoxIndicator`

Module contents
===============

"""

from abc import ABC, abstractmethod
from configparser import SafeConfigParser
from logging import getLogger

from eye.colorutils import QColorAlpha
from eye.connector import category_objects, register_signal, register_setup, disabled
from eye.helpers._lexercolorgroups import get_id_and_aliases
from eye.helpers.styles import STYLES
from eye.lexers import styles_from_lexer

__all__ = (
	'set_enabled', 'use_scheme_file', 'add_scheme_file',
	'lexer_set_font_family', 'lexer_set_font_point_size',
	'apply_scheme_to_editor', 'apply_scheme_dict_to_editor',
	'apply_scheme_on_lexer_change', 'apply_scheme_on_create',
)

LOGGER = getLogger(__name__)

FG_ATTRS = frozenset(['fg', 'foreground', 'color'])
BG_ATTRS = frozenset(['bg', 'background'])

SCHEME = None

def new_scheme():
	parser = SafeConfigParser()
	parser.optionxform = str
	return parser

def fuzzy_equals(a, b):
	def norm(name):
		return name.lower().replace(' ', '_')
	return norm(a) == norm(b)

def get_style_by_desc(lexer, desc, fuzzy=False):
	for i in range(1 << lexer.styleBitsNeeded()):
		idesc = lexer.description(i)
		if idesc == desc or (fuzzy and fuzzy_equals(desc, idesc)):
			return i

def parse_bool(s):
	s = s.lower()
	if s in ['1', 'true', 'yes', 'y', 'on']:
		return True
	elif s in ['0', 'false', 'no', 'n', 'off']:
		return False
	raise ValueError('%r is not a boolean value' % s)

class UnsupportedModification(Exception):
	pass

class Modificator(ABC):
	def __init__(self, editor, key, strvalue):
		self.editor = editor
		self.key = key
		self.strvalue = strvalue

	@abstractmethod
	def set_font(self, attr, value, *args):
		...

	@abstractmethod
	def set_color(self, value, *args):
		...

	@abstractmethod
	def set_paper(self, value, *args):
		...

	def apply_generic(self, attr, *args):
		if attr == 'font':
			self.set_font('Family', self.strvalue, *args)
		elif attr == 'points':
			self.set_font('PointSizeF', float(self.strvalue), *args)
		elif attr == 'bold':
			self.set_font('Bold', parse_bool(self.strvalue), *args)
		elif attr == 'italic':
			self.set_font('Italic', parse_bool(self.strvalue), *args)
		elif attr == 'underline':
			self.set_font('Underline', parse_bool(self.strvalue), *args)
		elif attr in FG_ATTRS:
			self.set_color(QColorAlpha(self.strvalue), *args)
		elif attr in BG_ATTRS:
			self.set_paper(QColorAlpha(self.strvalue), *args)
		else:
			raise UnsupportedModification()

class LexerModificator(Modificator):
	def apply(self):
		tokenname, attr = self.key.split('.')

		lexer = self.editor.lexer()
		if not lexer:
			return

		if tokenname == '*':
			ids = styles_from_lexer(lexer).values()
		elif tokenname == '_default':
			ids = [self.editor.STYLE_DEFAULT]
		else:
			ids = get_id_and_aliases(lexer, tokenname)

		for id in ids:
			self.apply_one(id, attr)

	def apply_one(self, style_id, attr):
		lexer = self.editor.lexer()

		self.apply_generic(attr, lexer, style_id)

	def apply_generic(self, attr, lexer, style_id):
		if attr == 'eolfill':
			lexer.setEolFill(parse_bool(self.strvalue))
		else:
			super().apply_generic(attr, lexer, style_id)

	def set_color(self, qc, lexer, style_id):
		lexer.setColor(QColorAlpha(self.strvalue), style_id)

	def set_paper(self, qc, lexer, style_id):
		lexer.setPaper(QColorAlpha(self.strvalue), style_id)

	def set_font(self, font_attr, value, lexer, style_id):
		font = lexer.font(style_id)
		font_attr = 'set%s' % font_attr
		getattr(font, font_attr)(value)
		lexer.setFont(font, style_id)

class EditorModificator(Modificator):
	def apply(self):
		element, attr = self.key.split('.')
		self.apply_generic(attr, element)

	def apply_caret(self, attr, strvalue):
		if attr in FG_ATTRS:
			qc = QColorAlpha(strvalue)
			self.editor.setCaretForegroundColor(qc)
		elif attr in BG_ATTRS:
			qc = QColorAlpha(strvalue)
			self.editor.setCaretLineBackgroundColor(qc)
		else:
			raise UnsupportedModification('only elements in %s are supported for caret' % (FG_ATTRS + BG_ATTRS))

	def set_color(self, qc, element):
		attrs = {
			'text': 'setColor',
			'selection': 'setSelectionForegroundColor',
			'whitespace': 'setWhitespaceForegroundColor',
			'caret': 'setCaretForegroundColor',
			'hotspot': 'setHotspotForegroundColor',
			'matchedbrace': 'setMatchedBraceForegroundColor',
			'unmatchedbrace': 'setUnmatchedBraceForegroundColor',
			'margin': 'setMarginsForegroundColor',
		}

		getattr(self.editor, attrs[element])(qc)

	def set_paper(self, qc, element):
		attrs = {
			'text': 'setPaper',
			'selection': 'setSelectionBackgroundColor',
			'whitespace': 'setWhitespaceBackgroundColor',
			'caret': 'setCaretLineBackgroundColor',
			'hotspot': 'setHotspotBackgroundColor',
			'matchedbrace': 'setMatchedBraceBackgroundColor',
			'unmatchedbrace': 'setUnmatchedBraceBackgroundColor',
			'margin': 'setMarginsBackgroundColor',
		}

		getattr(self.editor, attrs[element])(qc)

	def set_font(self, font_attr, value, element):
		if element != 'text':
			raise UnsupportedModification('only "text" is supported')
		# margin.* lacks a Editor.marginsFont()

		font = self.editor.font()
		font_attr = 'set%s' % font_attr
		getattr(font, font_attr)(value)
		self.editor.setFont(font)

class IndicatorModificator(Modificator):
	def apply(self):
		name, attr = self.key.split('.')

		indicator = self.editor.indicators.get(name)
		if not indicator:
			indicator = self.editor.create_indicator(name, self.editor.PlainIndicator)

		self.apply_generic(attr, indicator)

	def apply_generic(self, attr, indicator):
		if attr == 'style':
			indicator.set_style(getattr(self.editor, self.strvalue))
		else:
			super().apply_generic(attr, indicator)

	def set_color(self, qc, indicator):
		indicator.set_color(qc)

	def set_paper(self, qc, indicator):
		indicator.setOutlineColor(qc)

	def set_font(self, *args):
		raise UnsupportedModification('font cannot be set for indicators')

class StyleModificator(Modificator):
	def apply(self):
		name, attr = self.key.split('.')

		self.apply_generic(attr, STYLES[name])

	def apply_generic(self, attr, style):
		if attr == 'eolfill':
			style.setEolFill(parse_bool(self.strvalue))
		else:
			super().apply_generic(attr, style)

	def set_font(self, font_attr, value, style):
		font = style.font()
		font_attr = 'set%s' % font_attr
		getattr(font, font_attr)(value)
		style.setFont(font)

	def set_color(self, qc, style):
		style.set_color(qc)

	def set_paper(self, qc, style):
		style.set_paper(qc)

def get_modificator(name):
	modificators = {
		'token': LexerModificator,
		'indicator': IndicatorModificator,
		'base': EditorModificator,
		'style': StyleModificator,
	}

	return modificators.get(name)

def apply_scheme_dict_to_editor(dct, editor):
	for key in sorted(dct):
		value = dct[key]
		try:
			styletype, subkey = key.split('.', 1)
		except ValueError:
			LOGGER.info('ignoring malformed style key %r', key)
			continue

		modificator_type = get_modificator(styletype)
		if modificator_type is None:
			LOGGER.info('ignoring unknown style type %r', styletype)
			continue

		mod = modificator_type(editor, subkey, value)
		try:
			mod.apply()
		except UnsupportedModification as exc:
			LOGGER.warning('%s is not supported: %s', key, exc)
			continue

		LOGGER.debug('applied %r=%r to %r', key, value, editor)

def apply_scheme_to_editor(parser, editor):
	lexer = editor.lexer()

	lexer_name = lexer.language() if lexer else 'None'
	for section in ('*', lexer_name):
		if parser.has_section(section):
			LOGGER.debug('using section %r for file %r', section, editor.path)

			dict_scheme = dict(parser.items(section))
			apply_scheme_dict_to_editor(dict_scheme, editor)

def use_scheme_file(path, apply_to_all=True):
	"""Use a color scheme file

	Reset current color scheme and load a new scheme file.

	:param path: color scheme file
	:param applyToAll: if True, apply to existing editor widgets
	"""
	global SCHEME

	if path is None:
		SCHEME = None
		LOGGER.info('unsetting scheme file')
		return

	SCHEME = None
	add_scheme_file(path, apply_to_all)

def add_scheme_file(path, apply_to_all=True):
	"""Load a color scheme file

	Unlike :any:`use_scheme_file`, it does not reset the current color scheme but adds new definitions.
	If a previous scheme file had some definitions which are redefined in the new scheme, they are replaced by the
	new one.

	:param path: color scheme file
	:param apply_to_all: if True, apply to existing editor widgets
	"""
	global SCHEME

	if SCHEME is None:
		LOGGER.info('starting with empty scheme')
		SCHEME = new_scheme()

	LOGGER.info('adding scheme %r', path)
	SCHEME.read([path])

	if apply_to_all:
		for ed in category_objects('editor'):
			apply_scheme_to_editor(SCHEME, ed)

@register_setup('editor')
@disabled
def apply_scheme_on_create(editor):
	if SCHEME:
		apply_scheme_to_editor(SCHEME, editor)

@register_signal('editor', 'lexer_changed')
@disabled
def apply_scheme_on_lexer_change(editor, lexer):
	if SCHEME:
		apply_scheme_to_editor(SCHEME, editor)

def set_enabled(enabled=True):
	"""Enabled/disable automatic color scheme application to editors"""
	apply_scheme_on_create.enabled = enabled
	apply_scheme_on_lexer_change.enabled = enabled

def _lexer_set_font(lexer, cb, style):
	if style >= 0:
		font = lexer.font(style)
		cb(font)
		lexer.setFont(font, style)
		return

	for i in range(1 << lexer.styleBitsNeeded()):
		desc = lexer.description(i)
		if desc:
			_lexer_set_font(lexer, cb, i)

def lexer_set_font_family(lexer, family, style=-1):
	"""Set the font family of a lexer

	Set just the font family for `style` of `lexer`.
	Like :any:`QsciLexer.setFont`, but only change the font family, not font size or weight.

	:param lexer: the lexer to change
	:type lexer: QsciLexer
	:param family: font family to use
	:param style: if negative, modifies all styles of `lexer`
	"""
	def cb(font):
		font.setFamily(family)

	_lexer_set_font(lexer, cb, style)

def lexer_set_font_point_size(lexer, size, style=-1):
	"""Set the font size of a lexer

	Set just the font size for `style` of `lexer`.
	Like :any:`QsciLexer.setFont`, but only change the font size, not font family or weight.

	:param lexer: the lexer to change
	:type lexer: QsciLexer
	:param size: font size to use (in points)
	:param style: if negative, modifies all styles of `lexer`
	"""
	def cb(font):
		font.setPointSize(size)

	_lexer_set_font(lexer, cb, style)


"""
    Route mapping for the Resource API
"""
from typing import List
import pathlib
from fastapi import APIRouter, Depends, File, UploadFile, Form
from fastapi.exceptions import HTTPException
from sqlalchemy.orm import Session
from sqlalchemy.orm.exc import NoResultFound
from api.config import get_settings, get_db
from api.db import models
from api.routers import schemas
from api.routers.auth import get_current_active_user
from api.routers.helpers import (
  convert_resource_file
)
from api.queries.resource_queries import (
  get_entity,
  get_entities_by_name,
  save_entity,
  publish_entity,
  all_entities,
  all_entity_paths,
  all_entities_by_technology,
  clone_variation,
  save_variation,
  clear_all_tables
)
from api.queries.workbook_queries import (
  save_workbook
)
from api.transform import (
  transform,
  populate,
  convert_vmas_to_binary
)

settings = get_settings()
router = APIRouter()
DATADIR = pathlib.Path(__file__).parents[0].joinpath('data')

entity_mapping = {
  'scenario': models.Scenario,
  'reference': models.Reference,
  'cluster': models.Cluster,
  'population': models.Population,
  'variation': models.Variation,
  'vma': models.VMA,
  'adoption_data': models.AdoptionData,
  'tam': models.TAM,
  'ca_pds': models.CustomAdoptionPDS,
  'ca_ref': models.CustomAdoptionRef
}

@router.get('/resource/vma/info/{technology}/{name}',
  summary="Get the VMA info for a given technology",
  description="VMA info exists when VMA sources cannot be viewed." +
      "Note that there may not be existing VMA info for every technology.",
  tags=["Resource"]
  )
async def get_vma_info(name: str, technology: str, database: Session = Depends(get_db),
  db_active_user: models.User = Depends(get_current_active_user)):
  """
    VMA info exists when VMA sources cannot be viewed.
        Note that there may not be existing VMA info for every technology.

    Parameters:
    ------
    technology: str
      given technology name
    database: Session
      database session, defaults to initialize session
  """
  return get_entities_by_name(database, name, technology, models.VMA, db_active_user)

@router.get('/resource/vma/all/{technology}',
  summary="Get all the VMA data for a given technology",
  description="Returns all VMA data for a technology",
  tags=["Resource"]
  )
async def get_vma_all(technology: str, database: Session = Depends(get_db),
  db_active_user: models.User = Depends(get_current_active_user)):
  """
    Get all the VMA data for a given technology

    Parameters:
    ------
    technology: str
      given technology name
    database: Session
      database session, defaults to initialize session
  """
  return get_entities_by_name(database, f'solution/{technology}/%.csv', technology, models.VMA, db_active_user)

@router.get('/resource/{entity}s/full', response_model=List[schemas.ResourceOut],
  summary="Get the full resource (all data) of an entity by name",
  tags=["Resource"]
  )
async def get_all(entity: models.EntityName, database: Session = Depends(get_db),
  db_active_user: models.User = Depends(get_current_active_user)):
  """
    Get the full resource (all data) of an entity by name

    Parameters:
    ------
    entity: EntityName
      resource entity, maps to the EntityName enum
    database: Session
      database session, defaults to initialize session
    db_active_user: User
      current logged in user
  """
  return all_entities(database, entity_mapping[entity], db_active_user)

@router.get('/resource/{entity}s/paths', response_model=List[str],
  summary="Get the resource paths (no data) of an entity by name",
  tags=["Resource"]
  )
async def get_all_paths(entity: models.EntityName, database: Session = Depends(get_db)):
  """
    Get the resource paths (no data) of an entity by name

    Parameters:
    -----
    entity: EntityName
      resource entity, maps to the EntityName enum
    database: Session
      database session, defaults to initialize session
  """
  return all_entity_paths(database, entity, entity_mapping[entity])

@router.get('/resource/{entity}s/{technology}', response_model=dict,
  summary="Get resource entity by name",
  tags=["Resource"]
  )
async def get_by_technology(entity: models.EntityName, technology: str,
  database: Session = Depends(get_db), db_active_user: models.User = Depends(get_current_active_user)):
  """
    Get resource entity by name, returns a map of { entity_name: reference_path }

    Parameters:
    ------
    entity: EntityName
      resource entity, maps to the EntityName enum
    name: str
      resource entity name
    database: Session
      database session, defaults to initialize session
  """

  if entity in (models.EntityName.scenario, models.EntityName.reference):
    technology = 'n/a'

  entities = all_entities_by_technology(database, entity_mapping[entity], technology, db_active_user)
  result = dict()

  for s_entity in entities:
    result[s_entity.name] = f"/resource/{entity}/{s_entity.id}"

  return result

@router.post('/resource/{entity}/{technology}',
  summary="Uploads a new resource data of an entity by name",
  description="Uploads a new resource data of an entity by name. Accepts an Excel file" +
    "with a valid format",
  tags=["Resource"]
  )
async def post_resource_data(
    technology: str,
    entity: models.EntityName, file: UploadFile = File(...),
    name: str = Form(...), database: Session = Depends(get_db),
    db_active_user: models.User = Depends(get_current_active_user)):
  """
    Uploads a new resource data of an entity by name. Accepts CSV and Json object

    Parameters:
    ----
    entity: EntityName
      resource entity, maps to the EntityName enum
    file: UploadFile
      Uploaded Excel file
    name: str
      Name intended for resource
    database: Session
      database session, defaults to initialize session
  """
  if file.content_type not in [
    "text/comma-separated-values", "text/csv", "application/csv",
    "application/excel", "application/vnd.ms-excel", "application/vnd.msexcel",
    "application/json"]:
    raise HTTPException(status_code=400, detail="Invalid document type")

  resource_obj = convert_resource_file(file)

  # TODO: object validation when saved to DB
  return save_entity(database, name, technology, resource_obj, entity_mapping[entity], db_active_user)

@router.get('/resource/{entity}/{input_id}', response_model=schemas.ResourceOut,
  summary="Get resource entity by id",
  tags=["Resource"]
  )
async def get_by_id(entity: models.EntityName, input_id: int, database: Session = Depends(get_db)):
  """
    Get resource entity by id

    Parameters:
    ------
    entity: EntityName
      resource entity, maps to the EntityName enum
    input_id: int
      desired resource entity id
    database: Session
      database session, defaults to initialize session
  """
  return get_entity(database, input_id, entity_mapping[entity])

@router.put('/resource/{entity}/{input_id}/publish',
  summary='publish resource to be public',
  description='publishing resource by entity to be public, if public, the resource ' +
    'are then accessable for other users',
  tags=["Resource"])
async def publish_resource_by_id(entity: models.EntityName, input_id: int,
  database: Session = Depends(get_db),
  db_active_user: models.User = Depends(get_current_active_user)):
  """
    Publishing an entity to make it public by its entity id and type

    Parameters:
    ----
    entity: EntityName
      resource entity, maps to the EntityName enum
    input_id: str
      id of entity
    database: Session
      database session, defaults to initialize session
    db_active_user: User
      logged in user
  """
  try:
    return publish_entity(database, entity_mapping[entity], input_id, db_active_user, True)
  except NoResultFound:
    raise HTTPException(status_code=404, detail=entity + " not found")

@router.delete('/resource/{entity}/{input_id}/publish',
  summary='unpublish resource to be private',
  description='unpublish a public resource to and make it accessable only to author',
  tags=["Resource"])
async def unpublish_resource_by_id(entity: models.EntityName, input_id: int,
  database: Session = Depends(get_db),
  db_active_user: models.User = Depends(get_current_active_user)):
  """
    Unpublish an entity to make it private by its entity id and type

    Parameters:
    ----
    entity: EntityName
      resource entity, maps to the EntityName enum
    input_id: str
      id of entity
    database: Session
      database session, defaults to initialize session
    db_active_user: User
      logged in user
  """
  try:
    return publish_entity(database, entity_mapping[entity], input_id, db_active_user, False)
  except NoResultFound:
    raise HTTPException(status_code=404, detail=entity + " not found")

@router.post('/variation/fork/{input_id}', response_model=schemas.VariationOut,
  summary="Fork a variation with given id",
  description="Finds the variation by id, cloning and override the value before saving it" +
    "back to the database.",
  tags=["Resource"]
  )
async def fork_variation(input_id: int, patch: schemas.VariationPatch, database: Session = Depends(get_db)):
  """
    Finds the variation by id, cloning and override the value before saving it
    back to the database.

    Parameters:
    ----
    input_id: str
      variation id
    patch: schemas.VariationPatch
      overidding value of Variation object
    database: Session
      database session, defaults to initialize session
  """
  try:
    cloned_variation = clone_variation(database, input_id)
  except:
    raise HTTPException(status_code=400, detail="Variation not found")

  if patch.scenario_parent_path is not None:
    cloned_variation.data['scenario_parent_path'] = patch.scenario_parent_path
  if patch.scenario_parent_path is not None:
    cloned_variation.data['reference_parent_path'] = patch.reference_parent_path
  if patch.cluster_parent_path is not None:
    cloned_variation.data['cluster_parent_path'] = patch.cluster_parent_path
  if patch.population_parent_path is not None:
    cloned_variation.data['population_parent_path'] = patch.population_parent_path
  if patch.scenario_vars is not None:
    cloned_variation.data['scenario_vars'] = patch.scenario_vars
  if patch.reference_vars is not None:
    cloned_variation.data['reference_vars'] = patch.reference_vars
  if patch.cluster_vars is not None:
    cloned_variation.data['cluster_vars'] = patch.cluster_vars
  if patch.population_vars is not None:
    cloned_variation.data['population_vars'] = patch.population_vars
  if patch.vma_sources is not None:
    cloned_variation.data['vma_sources'] = patch.reference_vars

  return save_variation(database, cloned_variation)

@router.post('/variation', response_model=schemas.VariationOut,
  summary="Create a new variation",
  description="Note: the variation is not automatically added to the workbook." +
    "Use `POST /workbook/{id}/variation` to add a variation to a workbook.",
  tags=["Resource"]
  )
async def post_variation(variation: schemas.VariationIn, database: Session = Depends(get_db)):
  """
    Create a new variation from input, variation is not automatically added to workbook.

    Parameters:
    ----
    patch: schemas.VariationPatch
      overidding value of Variation object
    database: Session
      database session, defaults to initialize session
  """
  new_variation = models.Variation(
    name=variation.name,
    data={},
  )
  new_variation.data['scenario_parent_path'] = variation.scenario_parent_path
  new_variation.data['reference_parent_path'] = variation.reference_parent_path
  new_variation.data['cluster_parent_path'] = variation.cluster_parent_path
  new_variation.data['population_parent_path'] = variation.population_parent_path
  new_variation.data['scenario_vars'] = variation.scenario_vars
  new_variation.data['reference_vars'] = variation.reference_vars
  new_variation.data['cluster_vars'] = variation.cluster_vars
  new_variation.data['population_vars'] = variation.population_vars
  new_variation.data['vma_sources'] = variation.vma_sources
  return save_variation(database, new_variation)

@router.get("/initialize",
  summary="Initialize the database with data",
  description="""
    Puts default scenario, reference, VMA, etd data into the database.
    Creates corresponding workbook for each scenario. In production, initialization is only allowed once.
  """,
  tags=["Resource"]
  )
async def initialize(database: Session = Depends(get_db)):
  """
    Initialize the database with data

    Parameters:
    ----
    database: Session
      database session, defaults to initialize session
  """
  if database.query(models.VMA).count() > 0:
    if settings.is_production:
      raise HTTPException(status_code=400, detail="Database already initialized")

    clear_all_tables(database)

  [scenario_json, references_json, cluster_json, population_json] = transform()

  # create base scenario
  canonical_scenarios = ['drawdown-2020', 'plausible-2020', 'optimum-2020']
  for canonical_scenario in canonical_scenarios:
    scenario = save_entity(database, canonical_scenario, "n/a", scenario_json, models.Scenario)
    reference = save_entity(database, canonical_scenario, "n/a", references_json, models.Reference)
    cluster = save_entity(database, canonical_scenario, "n/a", cluster_json, models.Cluster)
    population = save_entity(database, canonical_scenario, "n/a", population_json, models.Population)
    variation = models.Variation(
      name='default',
      data={
        "scenario_parent_path": scenario.path,
        "reference_parent_path": reference.path,
        "cluster_parent_path": cluster.path,
        "population_parent_path": population.path,
        "scenario_vars": {},
        "reference_vars": {},
        "cluster_vars": {},
        "population_vars": {},
        "vma_sources": {}
      }
    )
    save_variation(database, variation)
    variation_dict = variation.__dict__['data']
    workbook = models.Workbook(
      name=canonical_scenario,
      description=canonical_scenario + " one of the canonical scenarios.",
      ui={},
      regions=['World'],
      start_year=2014,
      end_year=2060,
      variations=[
        variation_dict
      ]
    )
    _ = save_workbook(database, workbook)

  # populate resource tables:
  resource_models = [
    ('vma_data', models.VMA),
    ('tam', models.TAM),
    ('ad', models.AdoptionData),
    ('ca_pds_data', models.CustomAdoptionPDS),
    ('ca_ref_data', models.CustomAdoptionRef)
  ]
  for (directory, model) in resource_models:
    resources = populate(directory)
    for res in resources:
      tokens = res['technology'].split('/')
      save_entity(database, res['filename'], tokens[len(tokens) - 1], res['data'], model)

  vmas = convert_vmas_to_binary()
  for vma in vmas:
    tokens = vma['filename'].split('/')
    vma_csv = models.VMA_CSV(
      name=tokens[len(tokens) - 1],
      technology=vma['technology'],
      variable=vma['path'],
      legacy_variable=vma['legacy_variable'],
      original_filename=vma['filename'],
      data=vma['data']
    )
    database.add(vma_csv)
  database.commit()


import numpy as np

n = int(input())
list1 = []

while True:
    t = input()
    list1.append(tuple(map(int, t.split())))
    if t[0] == '0':
        break

price = [item for t in list1 for item in t]

for i in range(len(price) - 1, -1, -1):
    if i % 2 == 0:
        del price[i]

T = np.zeros((len(price), n + 1))

for i in range(0, len(price)):
    for j in range(0, n + 1):

        # First column => 0 length of rod => 0 profit
        if j == 0:
            continue

            # First row => T[i-1,j] doesn't exist so just pick the second value
        elif i == 0:
            T[i, j] = price[i] + T[i, j - i - 1]

            # where j <= i => T[i, j-i-1] doesn't exist so just pick the first value
        elif (j - i - 1) < 0:
            T[i, j] = T[i - 1, j]

            # using the whole expression
        else:
            T[i, j] = max(T[i - 1, j], (price[i] + T[i, j - i - 1]))

        # Answer in the extreme bottom right cell
x = T[len(price) - 1, n]
Ans = int(x)
print(Ans)


from pathlib import Path

lines = Path('input.txt').read_text().split('\n')[:-1]

class A:
    meaning = 'rock'
    win_value = 1
    beats = 'scissors'
    outcomes = {'lose': 'scissors','draw': 'rock','win': 'paper'}

class B:
    meaning = 'paper'
    win_value = 2
    beats = 'rock'
    outcomes = {'lose': 'rock','draw': 'paper','win': 'scissors'}

class C:
    meaning = 'scissors'
    win_value = 3
    beats = 'paper'
    outcomes = {'lose': 'paper','draw': 'scissors','win': 'rock'}

outcomes = {
    'win': 6,
    'draw': 3,
    'lose': 0,
    }

items = {
    'A': A,
    'B': B,
    'C': C,
    'rock': A,
    'paper': B,
    'scissors': C,
    'X': 'lose',
    'Y': 'draw',
    'Z': 'win',
    }

def compare(me, them):
    if me.meaning == them.meaning:
        return 'draw'
    elif me.beats == them.meaning:
        return 'win'
    else:
        return 'lose'

total = 0
for line in lines:
    them, me = line.split()
    them = items[them]()
    me = items[me] #win or lose
    me = them.outcomes[me] #rock paper scissors
    me = items[me] #my object
    score = me.win_value + outcomes[compare(me,them)]
    total += score
print(f'total is {total}')


from urllib.request import urlopen
from urllib.error import HTTPError, URLError
from bs4 import BeautifulSoup

def html_to_text_img(url):
    """
    reads html and parse into text and img data

    Parameter
    ------------
    url: str
        a url

    Returns
    -------------
    title: str
        title of an article
    text: str
        article contents
    img_url: str
        url of an image

    """
    if url is '' or url is None:
        return None
    try:
        html = urlopen(url)
    except HTTPError as e:
        print(e)
        return None
    except URLError as e:
        print(e)
        return None

    try:
        soup = BeautifulSoup(html.read(), "html.parser")
    except AttributeError as e:
        print(e)
        return None

    title = soup.find("h1").get_text()
    text = ''
    ps = soup.find("div", {"class": "article gtm-click"}).find_all('p')
    for p in ps:
        text += ''.join(p.get_text())
    imgs = soup.select("div.article__image img")
    img_url = imgs[0]['data-src']

    return title, text, img_url


def puzzle_to_lists(puzzle):
    val = 0
    full_cage = []
    for row in range(10):
        res = []
        for col in range(10):
            res.append(puzzle[col])
        puzzle = puzzle[10:]
        full_cage.append(res)
    return full_cage
def checkRows(puzzle, word):
   row_answer = []
   for row in range(len(puzzle)):
      list1 = []
      for col in range(len(puzzle)):
         arg = puzzle[row][col]
         list1.append(arg)
      list1 = ''.join(list1)
      if list1.find(word) != -1:
         row_answer.append(row)
         row_answer.append(list1.find(word))
         return row_answer
   return False
def checkColumns(puzzle, word):
    col_answer = []
    for row in range(len(puzzle)):
        list1 = []
        for col in range(len(puzzle)):
            arg = puzzle[col][row]
            list1.append(arg)
        list1 = ''.join(list1)
        if list1.find(word) != -1:
           col_answer.append(row)
           col_answer.append(list1.find(word))
           col_answer[0], col_answer[1] = col_answer[1], col_answer[0]
           return col_answer
    return False
def checkColumnsBackwards(puzzle,word):
    col_backwards = []
    for row in range(len(puzzle)):
        list1 = []
        for col in range(len(puzzle)):
            arg = puzzle[col][row]
            list1.append(arg)
        list1 = list1[::-1]  #google
        list1 = ''.join(list1)
        if list1.find(word) != -1:
            col_backwards.append(row)
            col_backwards.append(9-list1.find(word))
            col_backwards[0], col_backwards[1] = col_backwards[1], col_backwards[0]
            return col_backwards
    return False
def checkRowBackwards(puzzle, word):
    row_back = []
    for row in range(len(puzzle)):
        list1 = list(puzzle[row])
        list1 = list1[::-1] #google
        list1 = ''.join(list1)
        if list1.find(word) != -1:
           row_back.append(row)
           row_back.append(9-list1.find(word))
           return row_back
    return False
def checkDiagonal(puzzle, word):
 diagonal = []
 for row in range(len(puzzle)):
   for col in range(len(puzzle)):
      list1 = []
      i = 0
      while (row + i < 10 and col + i < 10):
         arg = puzzle[row + i] [col + i]
         list1.append(arg)
         i+=1
      list1 = ''.join(list1)
      if list1.find(word)!= -1:
         i = list1.find(word)
         diagonal.append(row+i)
         diagonal.append(col+i)
         return diagonal
 return False


import pandas as pd
import re
from time import sleep
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import argparse

# This script requires the following packages:
# pandas, openpyxl

parser = argparse.ArgumentParser()
parser.add_argument('-m', metavar='Max Age', action='store', default=7, type=int, help='Skips rows older than this (Default 7)')
parser.add_argument('-r', dest='reporter', type=str, metavar='Reporter', default='rsalazar15', action='store', help='Jira reporter')
parser.add_argument('-a', dest='assignee', type=str, metavar='Assignee', default='rsalazar15', action='store', help='Jira assignee')
parser.add_argument('-d', dest='delay', type=int, metavar='Delay', default=2, action='store', help='Delay between emails in seconds (Default 2)')
group = parser.add_mutually_exclusive_group()
group.add_argument('-v', '--verbose', action='store_true')
group.add_argument('-q', '--quiet', action='store_true')
parser.add_argument('files', nargs='*', type=str, help="List of Execl files")

def fix_name(name):
    temp = name.split(", ", 1)
    return f"{temp[1]} {temp[0]}"

def send_mail(arg):
    ois_email = ""
    jira_email = ""
    service_desk_email = ""

    for n in range(0, len(arg.files)):
        if arg.verbose:
            print("Reading from file %i" % n)
        # Read data from the spreadsheet
        in_file = arg.files[n]
        try:
            data = pd.read_excel(in_file)
        except FileNotFoundError:
            print("%s not Found" % arg.files[n])
            exit(1)

        # Iterate through each row of the spreadsheet
        size = data.shape[0]
        for row in range(0, size):
            # skip rows older than 7 days by default
            if int(data.iloc[row, 10]) > arg.m:
                if arg.verbose:
                    print("Skipping row %i" % row)
                continue

            # Check if student or Faculty
            if data.iloc[row, 5] == "Student":
                template_file = "Student.html"
            else:
                template_file = "Faculty.html"

            # Determine Course Title and acronym
            course_title = data.iloc[row, 8]
            if course_title == "Information Security Awareness":
                course_acronym = "ISA"
            elif course_title == "FERPA":
                course_acronym = "FERPA"
            elif course_title == "Digital Accessibility Awareness - TAMUCC":
                course_title = "Digital Accessibility Awareness"
                course_acronym = "DAA"
            else:
                raise Exception("Unknown Training type on row %s" % row)

            # Read the template into the email body
            template = open(template_file)
            name = fix_name(data.iloc[row, 0])
            body = template.read()
            template.close()

            # Replace placeholders with data from spreadsheet
            body = re.sub("NAME", name, body)
            body = re.sub("REPTR", arg.reporter, body)
            body = re.sub("ASIGN", arg.assignee, body)
            body = re.sub("UID", str(data.iloc[row, 1]), body)
            body = re.sub("ESD", str(data.iloc[row, 2]), body)
            body = re.sub("LPD", str(data.iloc[row, 3]), body)
            body = re.sub("TCD", str(data.iloc[row, 4]), body)
            body = re.sub("SSFD", str(data.iloc[row, 5]), body)
            body = re.sub("EMAIL", str(data.iloc[row, 6]), body)
            body = re.sub("PCID", str(data.iloc[row, 7]), body)
            body = re.sub("CT", str(data.iloc[row, 8]), body)
            body = re.sub("TDD", str(data.iloc[row, 9]), body)
            body = re.sub("DPD", str(data.iloc[row, 10]), body)
            body = re.sub("PDR", str(data.iloc[row, 11]), body)
            body = re.sub("DESC", str(data.iloc[row, 12]), body)
            body = re.sub("ADLOC", str(data.iloc[row, 13]), body)
            body = re.sub("SLOC", str(data.iloc[row, 14]), body)
            body = re.sub("SUPER", str(data.iloc[row, 15]), body)
            body = re.sub("SMAIL", str(data.iloc[row, 16]), body)
            body = re.sub("CRSTITLE", course_title, body)
            body = re.sub("CRSA", course_acronym, body)

            # Prepare Message
            msg = MIMEMultipart()
            body = MIMEText(body, 'html')
            msg.attach(body)
            msg['From'] = ois_email
            msg['Subject'] = course_title + " training for " + name
            msg['To'] = str(data.iloc[row, 6])
            msg['CC'] = "%s; %s" % (str(data.iloc[row, 16]), service_desk_email)
            msg['BCC'] = ois_email

            # Send Email
            s = smtplib.SMTP('smtp.tamucc.edu')
            s.send_message(msg)
            s.quit()

            if not arg.quiet:
                print("Sent %s email to: %s" % (course_acronym, name))

            # Avoid Spam Filter
            sleep(arg.delay)

    print("DONE")

if __name__ == '__main__':
    args = parser.parse_args()
    if args.files:
        send_mail(args)
    else:
        print("No files specified.")


'''
Get distribution of positive and negative sentiment probability for supplied text.
'''

from nltk.tokenize import casual_tokenize
import build_model, helpers

def get_sentiment(classifier, tweets, keep_status=True):
	'''
	Takes tweets as a list of dictionaries.
	Returns tweets as a list of dictionaries with sentiment labels.

	Arguments:
		classifier: NLTK Naive Bayes Classifier object.
		tweets: Tweets as a list of dictionaries.
		keep_status: Set as False to remove tweet status and reduce data size,
			keeping sentiment distribution and label.
	Returns:
		tweets: Tweets as a list of dictionaries.
	'''

	print('Starting text analysis...')
	print('Scoring tweets...')
	# get positive and negative probabilities for each tweet
	for tweet in tweets:
		custom_tokens = helpers.remove_noise(casual_tokenize(tweet['status']))
		dist = classifier.prob_classify(dict([token, True] for token in custom_tokens))

		# append probabilities to list
		pos_probability = dist.prob('Positive')
		neg_probability = dist.prob('Negative')

		# add sentiment probabilities to tweet dictionary
		try:
			tweet['positive'] = pos_probability
			tweet['negative'] = neg_probability
		except Exception as e:
			print(e)

		# add sentiment label to tweet dictionary
		if pos_probability >= 0.9:
			tweet['label'] = 'Very Positive'
		elif pos_probability >= 0.7:
			tweet['label'] = 'Positive'
		elif pos_probability > 0.3 and neg_probability > 0.3:
			tweet['label'] = 'Neutral'
		elif neg_probability >= 0.9:
			tweet['label'] = 'Very Negative'
		elif neg_probability >= 0.7:
			tweet['label'] = 'Negative'
		else:
			tweet['label'] = 'None'

		# optional: remove tweet status to reduce data size (keeping sentiment distribution and label)
		if tweet['status'] and keep_status == False:
			del tweet['status']

	print('Text analysis complete!\n')

	return tweets


'''
Created on May 24, 2016

@author: carlos
'''
import sys,os,cPickle,time
from Qt import QtWidgets,QtGui, QtCore
#from PySide import QtGui, QtCore

helpDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if not helpDir in sys.path:sys.path.append(helpDir)
from helpers import iconLib
from itertools import chain

class treeModel(QtCore.QAbstractItemModel):
    sortRole = QtCore.Qt.UserRole
    filterRole = QtCore.Qt.UserRole + 1
    matchRole = QtCore.Qt.UserRole + 2

    def __init__(self,data,log,parent=None):
        super(treeModel,self).__init__(parent)
        self.log = log
        self._data = data
        self._columnNames = ['Node Name']

    def supportedDropActions(self):
        return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction

    def flags(self, index):
        defaultFlags =  QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable

        if index.isValid():return QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsDragEnabled | defaultFlags

        return QtCore.Qt.ItemIsDropEnabled | defaultFlags

    def getNode(self,index):
        if index == None:return self._data
        if index.isValid():
            node = index.internalPointer()
            if node:
                return node
        return self._data

    def rowCount(self,parent):
        if not parent.isValid():
            parentNode = self._data
        else:
            parentNode = parent.internalPointer()

        return len(parentNode.children)

    def columnCount(self,parent):
        return len(self._columnNames)

    def data(self,index,role):

        if not index.isValid():return None

        node = index.internalPointer()
        if node.isDeleted:return
        column = index.column()
        row = index.row()

        if column == 0 and role == QtCore.Qt.CheckStateRole:
            if node.isHidden:return QtCore.Qt.Unchecked
            return QtCore.Qt.Checked

        if role == QtCore.Qt.DecorationRole:
            icon = iconLib.getIcon(node.iconName())
            if icon:
                pixmap = QtGui.QPixmap(icon,'1')
                icon = QtGui.QIcon(pixmap)
                return icon

        if role in (QtCore.Qt.DisplayRole,self.sortRole,self.filterRole):
            return node.name

    def headerData(self,section,orientation,role):
        if role == QtCore.Qt.DisplayRole:return self._columnNames[section]

    def parent(self,index):
        node = index.internalPointer()
        parent = None
        try:
            parent = node.parent
        except:
            return None

        if not parent:parent = self._data
        if parent == self._data:return QtCore.QModelIndex()

        return self.createIndex(parent.row(),0,parent)

    def index(self,row,column,parent):
        parentNode = self.getNode(parent)
        childItem = None

        if len(parentNode.children) > row:childItem = parentNode.children[row]

        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QtCore.QModelIndex()

    def setData(self,index, data, role):
        if not index.isValid(): return False
        if role == QtCore.Qt.CheckStateRole:
            node = index.internalPointer()
            node.isHidden = not node.isHidden
            return True
        #role != QtCore.Qt.EditRole:return False

        return False

    def parentObjects(self,parent,children,skiScnP=False):
        pIn = None
        if parent.isValid():pIn = parent.internalPointer()
        if not pIn:pIn = self._data

        destInt = len(pIn.children)

        for c in children:
            pI = self.parent(c)
            cIn = c.internalPointer()
            if cIn.parent == pIn:return
            self.log.debug('-->moveRows1 %s %s %s %s %s' % (pI,c.row(),c.row(),parent,destInt))

            self.beginMoveRows(pI,c.row(),c.row(),parent,destInt)
            cIn.parent = pIn
            self.endMoveRows()
            destInt += 1

    def parentNode(self,node,parent):
        if not parent:parent = self._data
        self.log.debug('parenting %s to %s' % (node._data,parent._data))

        pI = self.indexFromNode(parent)
        nI = self.indexFromNode(node)

        papa = self.parent(nI)

        dest = len(parent.children)

        self.beginMoveRows(papa,nI.row(),nI.row(),pI,dest)
        node.parent(parent)
        self.endMoveRows()

    def indexFromNode(self,nd):
        if not nd.parent:return QtCore.QModelIndex()
        row = nd.parent.children.index(nd)
        indx = self.createIndex(row,0,nd)

        return indx

    def removeRow(self,nd):
        self.log.debug('model will now try to remove the row')
        index = self.indexFromNode(nd)
        parent = self.indexFromNode(nd.parent)
        self.log.debug('found qindex and parent qindex')

        self.log.debug('moving children to root')

        newP = self.createIndex(-1,-1,self._data)
        ch = nd.children[:]
        destInt = len(self._data.children)

        self.log.debug('moving rows')
        self.beginMoveRows(index,0,len(ch),newP,destInt)
        self.log.debug('parenting children to root')
        for c in ch:c.parent = self._data
        self.endMoveRows()

        r = index.row()
        self.beginRemoveRows(parent,r,r)
        self.log.debug('removing maxObjHelper parent')
        nd.parent = None
        self.log.debug('deleting maxObjHelper')
        del nd
        self.endRemoveRows()
        self.log.debug('done with removeRow Method...')

    def insertNode(self,node,parent):
        if not parent:parent = self._data
        #print 'inserting',node,'on parent',parent
        pI = self.indexFromNode(parent)
        pos = len(parent.children)
        self.beginInsertRows(pI,pos,pos)
        node.parent = parent
        self.endInsertRows()

    def getSelectedIndexs(self,proxy):
        #pyside and pyside 2 compatible
        try:
            out = QtGui.QItemSelection()
        except:
            out = QtCore.QItemSelection()

        chld = [self.index(i,0,None) for i in xrange(len(self._data.children))]
        first = None
        last = None

        while len(chld):
            grandChild = []
            for c in chld:
                node = c.internalPointer()
                for i in  xrange(len(node.children)):
                    grandChild.append(self.index(i,0,c))

                if node.isSelected:
                    mI = proxy.mapFromSource(c)
                    out.select(mI,mI)#,QtGui.QItemSelectionModel.Select)

            chld = grandChild
        return out


class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        # 如果cost为空，直接返回0
        if not cost:
            return 0

        n = len(cost)
        # dp[i]表示到达第i阶梯所需的最小费用
        dp = [0] * n
        dp[0] = cost[0]
        if len(cost) >= 2:
            dp[1] = cost[1]

        # 开始动态规划
        # 对于每一阶，计算到达该阶梯的最小费用
        for i in range(2, n):
            # 到达第i阶的最小费用是当前阶的费用加上前一阶和前两阶的最小值
            dp[i] = cost[i] + min(dp[i-1], dp[i-2])

        # 因为可以从最后一阶或倒数第二阶达到顶部，所以返回这两者的最小值
        return min(dp[-1], dp[-2])

    """
解题思路：

创建一个dp阵列，其每一项dp[i]代表到达第i阶梯所需的最小费用。
初始化dp[0]和dp[1]。
使用动态规划填充dp阵列，其中每一项的值是当前阶梯的费用加上前一阶和前两阶的最小值。
因为可以从最后一阶或倒数第二阶直接到达顶部，所以最后返回dp[-1]和dp[-2]中的最小值。

时间复杂度：O(n)，其中n是给定的阶梯数量，因为我们只遍历阶梯一次。
空间复杂度：O(n)，我们需要一个长度为n的dp阵列来存储每个阶梯的最小成本。
    """


from discord.ext import commands
import emoji
import discord
import re
from discord import RawReactionActionEvent
import json
import asyncio

path = ""
data = None

class EventHost(commands.Cog):

    def __init__(self, client):
        self.client = client # sets the client variable so we can use it in cogs'
        self._last_member = None

    @commands.Cog.listener()
    async def on_ready(self):
        global data , path

        path = 'bot/data/event_host.json'
        data = load_json(path)
        print("event_host online")

    #---互動表情新增選擇---
    @commands.Cog.listener()
    async def on_raw_reaction_add(self , payload: RawReactionActionEvent):
        global data , path

        if str(payload.message_id) not in data:
            return

        if str(payload.emoji) == "✅":
            data[str(payload.message_id)]["members"].append(str(payload.user_id))
            data = write_json(path,data)

            embed = refresh_embed(data , payload.message_id)

            channel = self.client.get_channel(payload.channel_id)
            message = await channel.fetch_message(payload.message_id)
            await message.edit(embed=embed)

        if len(data[str(payload.message_id)]["members"]) >= int(data[str(payload.message_id)]["goal"]):

            embed = refresh_embed(data , payload.message_id)

            channel = self.client.get_channel(payload.channel_id)
            message = await channel.fetch_message(payload.message_id)
            await message.edit(embed=embed)

            data.pop(str(payload.message_id),None)
            data = write_json(path,data)
            return

    #---互動表情移除選擇---
    @commands.Cog.listener()
    async def on_raw_reaction_remove(self , payload: RawReactionActionEvent):
        global data , path

        if str(payload.message_id) not in data:
            return

        if str(payload.emoji) == "✅":
            data[str(payload.message_id)]["members"].remove(str(payload.user_id))
            data = write_json(path,data)

            embed = refresh_embed(data , payload.message_id)

            channel = self.client.get_channel(payload.channel_id)
            message = await channel.fetch_message(payload.message_id)
            await message.edit(embed=embed)

    #---主持活動---
    @commands.command()
    async def event_host(self, ctx, title ,goal:int , time:int):
        global data , path

        if goal <= 0:
            await ctx.send("wrong format")
            await ctx.message.delete()
            return

        embed=discord.Embed(title="參加活動", description= "活動生成中",color=0x2ecc71)

        message = await ctx.send(embed=embed)

        await message.add_reaction("✅")

        message_id = message.id

        if message_id not in data:
            data.update({str(message_id):{"title":title,"time":time,"goal":goal,"members":[]}})
            data = write_json(path,data)

        await ctx.message.delete()

        while (1):
            if(data[str(message_id)]["time"] == 0):
                message = await ctx.fetch_message(message_id)

                embed = refresh_embed(data , message_id , True)

                await message.edit(embed=embed)
                data.pop(str(message_id),None)
                data = write_json(path,data)
                break

            else:
                message = await ctx.fetch_message(message_id)

                embed = refresh_embed(data , message_id)

                await message.edit(embed=embed)
                data[str(message_id)].update({"time":data[str(message_id)]["time"]-1})
                data = write_json(path,data)

                await asyncio.sleep(3600)

def refresh_embed(data , message_id , timeout = False):
    join_members = ""
    for i in data[str(message_id)]["members"]:
        join_members = join_members + f"<@{i}>\n"

    if join_members == "":
        join_members = "尚未有人加入"

    embed=discord.Embed(title="參加活動", description="" ,color=0x2ecc71)
    embed.add_field(name="活動名稱", value=data[str(message_id)]["title"] , inline=False)
    embed.add_field(name="目標人數", value=data[str(message_id)]["goal"] , inline=True)
    time_string = "`"+str(int(int(data[str(message_id)]["time"]/24)))+"`天`"+str(int(data[str(message_id)]["time"])%24)+"`小時"
    if int(int(data[str(message_id)]["time"]/24)) + int(data[str(message_id)]["time"])%24 == 0:
        time_string = "小於1小時"

    if timeout:
        time_string = "已超時"

    embed.add_field(name="剩餘時間", value= time_string , inline=True)
    embed.add_field(name="目前參與人員", value=join_members , inline=False)

    return embed

async def setup(client):
    await client.add_cog(EventHost(client))

def is_emoji(s):
    return s in emoji.UNICODE_EMOJI_ENGLISH

def is_discord_emoji(s):
	matched = re.match("^<:[a-z0-9_A-Z]+:[0-9]+>", s)
	return bool(matched)

def is_discord_role(s):
	matched = re.match("^<@&[0-9]+>", s)
	return bool(matched)

def write_json(path ,data):

    with open(path, 'w', encoding='utf-8') as f:
        json.dump(data, f, indent=4, ensure_ascii=False)
        f.close()

    with open(path, encoding='utf-8') as f:
        data = json.load(f)
        f.close()

    return (data)

def load_json(path):

    with open(path, 'r' , encoding="utf8") as f:
        data = json.load(f)

    return(data)


# https://pypi.org/project/pinyin/
# %%
import re
from pathlib import Path
import sys
import file_lib as f_lib
import pinyin

pinyin.get(u'你好')
pinyin.get('你好', format="strip", delimiter="")
pinyin.get('john smith', format="strip", delimiter="")

# %%
# ANCHOR 1. Loading data :

# setting up path to the data file

def load_data():
    file_name = Path(
        'C:\Local\Work\ML_Name\Code\Test\data\multi_class_tst_cn.csv')
    df = f_lib.load_csv_file(file_name)
    return(df)

# df = load_data()

# %%
# ANCHOR 3. Data Preprocessing
# Data Preprocessing :
# 1. Vectorization:  converting all kind input into unique digit vector as input matrix
# 3. Convert all output label into unique output

def data_preprocessing(df):
    # Func:
    out_put_file_name = Path("C:\Local\Work\ML_Name\Code\Test\multi_cn.csv")
    with open(out_put_file_name,   encoding='utf-8', mode="w") as file:
        file.write('code,name\n')
        for i in range(df['name'].size):
            code = list(df['code'])[i]
            x = list(df['name'])[i]
            x = pinyin.get(x, format="strip", delimiter="")
            x = x.title()
            file.write(f"{code},{x}\n")
    return(1)

# data_preprocessing(df)

# %%
if __name__ == '__main__':
    df = load_data()
    _ = data_preprocessing(df)
    print(_)

# %%


# -*- coding: utf-8 -*-
"""
Created on Tue Apr 26 22:33:38 2022

@author: salva
"""

import pandas as pd
import numpy as np
import pyphi_batch as phibatch
import matplotlib.pyplot as plt
import pyphi_plots as pp

bdata        = pd.read_excel('Batch Dryer Case Study.xlsx',sheet_name='Trajectories')
cqa          = pd.read_excel('Batch Dryer Case Study.xlsx',sheet_name='ProductQuality')
char         = pd.read_excel('Batch Dryer Case Study.xlsx',sheet_name='ProcessCharacteristics')
initial_chem = pd.read_excel('Batch Dryer Case Study.xlsx',sheet_name='InitialChemistry')
cat          = pd.read_excel('Batch Dryer Case Study.xlsx',sheet_name='classifiers')

phibatch.plot_var_all_batches(bdata)
#%%
samples={'Deagglomerate':20,'Heat':30,'Cooldown':40};
bdata_aligned_phase=phibatch.phase_simple_align(bdata,samples)
phibatch.plot_var_all_batches(bdata_aligned_phase,
                  plot_title='simple alignment /phase',
                  phase_samples=samples)
#%%
mpca_obj=phibatch.mpca(bdata_aligned_phase,3,phase_samples=samples)
pp.score_scatter(mpca_obj,[1,2],CLASSID=cat,colorby='Quality 2')

#%% Show a forecast
mon_data =phibatch.monitor(mpca_obj, bdata_aligned_phase,which_batch='Batch 5')

#Show forecast of x
var='Dryer Temp'
point_in_time=20
batch2forecast='Batch 5'
forecast=mon_data['forecast']
mdata=bdata_aligned_phase[bdata_aligned_phase['Batch number']==batch2forecast]

plt.figure()
f=forecast[point_in_time]
plt.plot(mdata[var][:point_in_time],'o',label='Measured')
aux=[np.nan]*point_in_time
aux.extend(f[var].values[point_in_time:].tolist())
plt.plot(np.array(aux),label='Forecast')
plt.plot(mdata[var][point_in_time:],'o',label='Known trajectory',alpha=0.3)
plt.xlabel('sample')
plt.ylabel(var)
plt.legend()
plt.title('Forecast for '+var+' at sample '+str(point_in_time)+' for '+batch2forecast )

#%% PLS with trajectories
mpls_obj_t=phibatch.mpls(bdata_aligned_phase,cqa,5,phase_samples=samples)
pp.r2pv(mpls_obj_t)
pp.score_scatter(mpls_obj_t, [1,2],CLASSID=cat,colorby='Quality')

#%% PLS with initial chemistry
mpls_obj_i=phibatch.mpls(bdata_aligned_phase,cqa,5,zinit=initial_chem,
                       phase_samples=samples)
pp.score_scatter(mpls_obj_i, [1,2],CLASSID=cat,colorby='Quality')

#%% PLS with initial chemistry MB

mpls_obj_i_mb=phibatch.mpls(bdata_aligned_phase,cqa,5,zinit=initial_chem,
                      phase_samples=samples,mb_each_var='True')
pp.score_scatter(mpls_obj_i_mb, [1,2],CLASSID=cat,colorby='Quality')
pp.mb_weights(mpls_obj_i_mb)
phibatch.loadings_abs_integral(mpls_obj_i_mb)
phibatch.loadings(mpls_obj_i_mb,1)
#What process reccomendation would we do to minimize off-spec product ?

plt.show()

Process Analytics Course Materials 2023/advanced_datasets/1 Batch Process Analysis/FMC Case Study/batch_dryer_case_study.py


import random

numbers = [15, 3, 64, 33, 22, 12, 45]
min = numbers[0]
for i in numbers:
    if i < min:
        min = i
print(min)
counter = 0
while counter < len(numbers):
    if numbers[counter] < min:
        min = numbers[counter]
    counter += 1
print(min)# 1.Знайти мінімальне число в списку

sum = 0
for i in numbers:
    if i % 3 == 0:
        sum += i
print(sum)

while counter < len(numbers):
    if numbers[counter] % 3 == 0:
        sum = numbers[counter]
    counter += 1
print(sum)# 2.Знайти суму всіх чисел в списку що діляться на 3

b = []
с = []
for i in range(10, 30):
    b.append(i ** 2)
print(b)

counter = 10
while counter < 10:
    b.append(b[counter] ** 2)
    counter += 1
print(b)# 3.Вивести квадрати чисел від 10 до 30

counter = 0
sum = 0
for i in numbers:
    sum += i
    counter += 1
print(sum / counter)
while counter < len(numbers):
    sum += numbers[counter]
    counter += 1
print(sum / counter)# 4.Вивести середнє значення в списку

n = int(input("enter the n "))
sum = 0
for i in range(1, n + 1):
    sum += 1 / i
print(sum)
while counter < n:
    sum += 1/numbers[counter]
    counter += 1
print(sum) # 5.Число n>2 вводиться з клавіатури, порахувати 1 + 1/2 + 1/3 + ... + 1/n

ran = 3
matruc = []

for i in range(ran):
    matruc.append([])
    for j in range(ran):
        matruc[i].append(random.randint(1, 10))
    print(matruc[i]) # 6.Згенерувати квадратну матрицю з довільними числами
print("___________________")
sumMain = 0
for i in range(ran):
    for j in range(ran):
        sumMain += matruc[j][i]
    sumMain -= matruc[i][i]    #Суммирует каждый столбик
    matruc[i][i] = sumMain #приравнивает диагональные элементы к сумме столбца n
    sumMain = 0 #Обнуляет сумму для повторного подсчета
    print(matruc[i]) # 7.Отриману матрицю в 6му завдання перетворити так щоб діагональний елемент дорівнював сумі елементів по вертикалі


"""
백준 3273번 두  수의 합
수열을 정렬한 뒤 풀이한다.
정렬된 수열 seq에 대하여 seq[l] + seq[r] = x 인 l과 r을 찾을 때
 l을 1부터 잡고 비교한다. 이때 다른 수 r은 가장 큰 수에서 시작한다.
 1.만약 seq[l] + seq[r] > x이면 r을 줄여가며 l과 비교한다.
 그런데 자연수 n에 대해 seq[l+n] + seq[r] > seq[l] + seq[r] > x이므로 l보다 더 큰 수를 대입할 때 r이상의 값은 대입할 필요가 없다.
 2.만약 seq[l] + seq[r] < x이면 r보다 작은 수 k에 대하여 seq[l] +seq[k] < seq[l] +seq[r] < x이므로 l에 대해 더이상 비교할 필요가
없으므로 l에 다음 수를 대입한다.
 3.만약 seq[l]과 seq[r]이 같으면 정답 카운터를 늘리고 중복된 값이 업으므로 l과 r이하의 수를 비교할 필요가 없다.그래서 다음 l값을 대입한다.
 1과 마찬가지로 r이상의 값에서는 더이상 고려할 필요없다.
 계속해서 대입을 하다 l과 r이 교차하는 순간부터는 더이상 비교를 할 필요가 없다.
"""
n = int(input())
seq = list(map(int,input().split()))
x = int(input())
l,r = 0, n-1
seq.sort()
ans = 0
while l <r:
    if seq[l] + seq[r] < x:
        l += 1
    elif seq[l] + seq[r] > x:
        r -= 1
    else:
        ans += 1
        l += 1
        r -= 1
print(ans)


import pickle
import numpy as np

train_file = open("train","wb")

# initialize writing object
obj = {}
obj["curr_it"] = 0
obj["eps"] = .3
obj["alpha"] = .5
obj["gamma"] = .5
obj["count"] = 0
obj["runs"] = np.array([])
obj[0] = {} # Q dictionary at every t runs

pickle.dump(obj,train_file,pickle.HIGHEST_PROTOCOL)

Machine Learning/Final Project - Snake AI (Python)/code/snake_old/reinit_training.py


# While Loops

# while <condition>:
#     1 -
#     2 -

counter = 0

while counter < 5:
    print(f'the count is: {counter}')
    counter = counter + 1

# ==================

# while (A and B) or C:
# while True:
#     if A:
#         break
#     ....

#     if B:
#         break

inp = input('Enter a number: ')
print(inp)

# operators in python
# int and float and string methods
# Hop Wiz (1 - 100)
# scape character ---> list
# format character
# prime numbers (1 - 1000) ---> for and else
# anything else to use in place of (enumerate and zip)
# for loop to iterate backwards
# for loop to iterate between odd elements and even elements
# force a user to input an integer
# calculate mean of unknown number of integers (q or quit to stop entering data)
# ------------------
# numberString = '76,876532,9.356,0,0.23,-234,6,87586,987,142526353526353526,6454737364654,-0.375'


# https://leetcode-cn.com/problems/merge-two-sorted-lists/

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution2:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        lll = self.gl(l1) + self.gl(l2)
        lll.sort()
        if len(lll) == 0:
            return l1
        root = ListNode(lll[0])
        p = root
        for i in lll[1:]:
            p.next = ListNode(i)
            p = p.next
        return root

    def gl(self, l):
        ll = []
        while l:
            ll.append(l.val)
            l = l.next
        return ll

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        p = ListNode(0)
        q = p
        while l1 and l2:
            if l1.val < l2.val:
                q.next = l1
                l1 = l1.next
            else:
                q.next = l2
                l2 = l2.next
            q = q.next
        q.next = l1 if l2 is None else l2
        return p.next


ar=[115,38,8,9,7,4,19,3,78,45];

def min(index,arr=[]):
    minValue=arr[index];
    sIndex=index
    for i in range(index+1,len(arr)):
        if minValue>arr[i]:
            minValue=arr[i]
            sIndex=i
    return sIndex

def selectionSort(array):
    for i in range(len(array)):
        k=min(i,array);
        temp=array[k];
        array[k]=array[i];
        array[i]=temp;

selectionSort(ar)

print(ar)


from typing import List

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        # okay.
        # binary search is all about looking in smaller version of the
        # original list by slicing the list or moving list index pointer
        # around. this particular one is set up for solution by recursion.

        # base case
        # not in the list
        if len(nums) == 0:
            return -1

        middle = len(nums) // 2
        index = 0

        # base case
        # target is the only one in the list
        if len(nums) == 1 and nums[0] == target:
            return middle

        if target < nums[middle]:
            index -= middle
            self.search(nums[0:middle], target)
        elif target == nums[middle]:
            return middle
        elif target > nums[middle]:
            index += middle
            self.search(nums[middle:], target)

        return middle

sol = Solution()
list_ = [-1,0,3,5,9,12]
print(sol.search(list_, -1))
print(sol.search(list_, 0))
print(sol.search(list_, 3))
print(sol.search(list_, 5))
print(sol.search(list_, 9))


import pygame
from sys import exit
import time
from utils import Sprite_group, Sprite_text, Sprite_button

def state_finish(screen, clock, game, colors, db_type, registrar, order_by, font_path, state, bindings, user_info):

    ### INIT STATE
    result_str     = ''
    local_pos_str  = ''
    global_pos_str = ''
    if not game.lose:
        if state[1] == 'marathon':
            result_str = f'score: {game.stats["score"]}'
        elif state[1] == 'sprint':
            minutes      = int(game.stats['time'] // 60)
            seconds      = int(game.stats['time'] % 60)
            milliseconds = int(game.stats['time'] % 1 * 1000)
            result_str = f'time: {minutes}:{seconds:02}.{milliseconds:03}'
        elif state[1] == 'blitz':
            result_str = f'score: {game.stats["score"]}'

        if user_info['username'] != 'guest':
            row = [user_info['username'], time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime()), time.strftime('%z', time.localtime())] + [game.stats[stat] for stat in game.stat_names]
            if db_type == 'csv':
                position = registrar.save(row, state[1], order_by[state[1]])
            elif db_type == 'sql' or db_type == 'msa':
                position = registrar.save(row, order_by[state[1]])

            if position[0] == 1:
                local_pos_str = 'new best!'
            elif position[0] % 10 == 1 and position[0] // 10 % 100 != 1:
                local_pos_str = f'{position[0]}st best'
            elif position[0] % 10 == 2 and position[0] // 10 % 100 != 1:
                local_pos_str = f'{position[0]}nd best'
            elif position[0] % 10 == 3 and position[0] // 10 % 100 != 1:
                local_pos_str = f'{position[0]}rd best'
            else:
                local_pos_str = f'{position[0]}th best'

            if position[1] == 1:
                global_pos_str = 'global record!'
            elif position[1] % 10 == 1 and position[1] // 10 % 100 != 1:
                global_pos_str = f'global {position[1]}st'
            elif position[1] % 10 == 2 and position[1] // 10 % 100 != 1:
                global_pos_str = f'global {position[1]}nd'
            elif position[1] % 10 == 3 and position[1] // 10 % 100 != 1:
                global_pos_str = f'global {position[1]}rd'
            else:
                global_pos_str = f'global {position[1]}th'

    finish_group = Sprite_group(
        title_text      = Sprite_text('midbottom', (0, -10), 'center', 'FINISH' if not game.lose else 'LOSE', (255, 255, 255), 4, font_path),
        result_text     = Sprite_text('midbottom', (0, -2), 'center', result_str, (255, 255, 255), 4, font_path),
        local_pos_text  = Sprite_text('midbottom', (0, 0), 'center', local_pos_str, (255, 255, 255), 2, font_path),
        global_pos_text = Sprite_text('midbottom', (0, 2), 'center', global_pos_str, (255, 255, 255), 2, font_path),
        retry_button    = Sprite_button('midleft', (1, 10), 'center', (8, 2), (0, 0, 0), (255, 255, 255), 2, 'retry', (255, 255, 255), 4, font_path),
        menu_button     = Sprite_button('midright', (-1, 10), 'center', (8, 2), (0, 0, 0), (255, 255, 255), 2, 'menu', (255, 255, 255), 4, font_path)
    )

    finish_group.resize(screen)

    while True:

        ### EVENT LOOP
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
            elif event.type == pygame.VIDEORESIZE:
                screen = pygame.display.set_mode(event.size, pygame.RESIZABLE)
                finish_group.resize(screen)
            elif event.type == pygame.KEYDOWN:
                if event.key == bindings['quit']:
                    state[0] = 'menu'
                    return
                elif event.key == bindings['reset']:
                    state[0] = 'countdown'
                    return
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    pos = pygame.mouse.get_pos()
                    if finish_group.get('retry_button').rect.collidepoint(pos):
                        state[0] = 'countdown'
                        return
                    elif finish_group.get('menu_button').rect.collidepoint(pos):
                        state[0] = 'menu'
                        return

        ### CLEAR SCREEN
        pygame.draw.rect(screen, colors['1'], screen.get_rect())

        ### DRAW SPRITES
        finish_group.draw(screen)

        ### CLOCK
        dim = min(screen.get_width() / 40, screen.get_height() / 30) # To fit in a 4:3 aspect ratio
        font = pygame.font.Font(font_path, round(.5 * 3 * dim))
        image = font.render(f'{round(clock.get_fps())}', False, (255, 255, 255))
        rect = image.get_rect(bottomright=(screen.get_width() - 1 * dim, 5 * dim))
        screen.blit(image, rect)

        pygame.display.update()
        clock.tick(60)


import numpy as np
import pandas as pd

from typing import Literal
from joblib import Parallel, delayed
from numpy.typing import ArrayLike, NDArray

from sklearn.exceptions import NotFittedError
from sklearn.metrics import precision_recall_curve
from sklearn.model_selection import train_test_split
from sklearn.utils.validation import check_is_fitted
from sklearn.linear_model._base import LinearClassifierMixin
from sklearn.base import BaseEstimator, ClassifierMixin, clone

from .metrics import confusion_score
from .general import Classifier, LinearClassifier

class ThresholdCompositeClassifier(BaseEstimator, ClassifierMixin):
    """
    Compute a threshold to apply to probabilities based on precision or recall on test data.

    This classifier wraps a binary classifier.
    Upon training, the data is split into a training set and a testing set. The classifier is
    fitted on the training set, and the precision and recall curves are calculated on the
    testing set. A threshold is then determined based on the desired value of one of those statistics.
    Upon prediction, the threshold is applied to the predicted probabilities to determine the
    predicted class.

    Parameters
    ----------
    classifier : classifier
        A binary classifier to wrap.

    value : float, default 0.5
        Value of the `statistic` used to determine the threshold.

    statistic : {'precision', 'recall'}, default 'precision'
        Statistic to use to determine the threshold.

    pos_label : int or str, default 1
        The label of the positive class, to which `statistic` refers.

    test_size : float or int, default 0.1
        Size of the test data set, taken out of the data given to `fit`.
        - float: Proportion of the data, between 0.0 and 1.0.
        - int: Absolute number of samples.

    Attributes
    ----------
    threshold_ : float
        The threshold applied to `statistic` upon prediction.
    """
    STATS = {       # index, reduction func, unattainable value
        'precision': (0,     np.min,          1.1),
        'recall':    (1,     np.max,         -0.1),
    }

    def __init__(
            self,
            classifier: Classifier,
            value: float = 0.5,
            *,
            statistic: Literal['precision', 'recall'] = 'precision',
            pos_label: int | str = 1,
            test_size: float | int = 0.1,
    ):
        self.classifier = classifier
        self.value = value
        self.statistic = statistic
        self.pos_label = pos_label
        self.test_size = test_size

    def fit(self, X, y, **fit_params):
        if self.statistic not in self.STATS:
            raise ValueError(f"Unknown statistic `{self.statistic}`. Possible values: {', '.join(self.STATS)}.")
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=self.test_size, stratify=y)

        self.classifier.fit(X_train, y_train, **fit_params)
        self.probmask_ = self.classifier.classes_ == self.pos_label
        self.srt_ = np.argsort(self.probmask_)

        # these values can be used for post-adjustment of threshold
        self.X_test_probs_ = self._probs(X_test)
        self.y_test_values_ = y_test.values

        self._update_threshold()
        return self

    def set_params(self, **params):
        super().set_params(**params)
        if 'value' in params:
            try:
                check_is_fitted(self, ['X_test_probs_', 'y_test_values_'])
            except NotFittedError:
                pass
            else:
                self._update_threshold()
        return self

    def _update_threshold(self):
        prt = precision_recall_curve(self.y_test_values_, self.X_test_probs_, pos_label=self.pos_label)
        ind, reduce_func, unattainable = self.STATS[self.statistic]
        stat = prt[ind][:-1]
        thresholds = prt[-1]
        valid = np.append(thresholds[stat >= self.value], unattainable)
        self.threshold_ = reduce_func(valid)

    def predict(self, X):
        check_is_fitted(self, ['probmask_', 'srt_', 'threshold_'])
        return self.classifier.classes_[self.srt_][(self._probs(X) >= self.threshold_).astype(int)]

    @property
    def classes_(self):
        return self.classifier.classes_

    def predict_proba(self, X):
        return self.classifier.predict_proba(X)

    def predict_log_proba(self, X):
        if hasattr(self.classifier, 'predict_log_proba'):
            return self.classifier.predict_log_proba(X)
        return np.log(self.predict_proba(X))

    def _probs(self, X):
        return self.predict_proba(X)[:, self.probmask_]

class WeightedLinearOVRClassifier(BaseEstimator, LinearClassifierMixin, ClassifierMixin):
    """
    A classifier which wraps a linear classifier and performs a weighted OVR strategy between the classes.

    For each class, the classifier is fitted separately, in parallel.
    The weights are determined by a price matrix, giving the price for each classification mistake,
    and the class probability of occurrence.

    Usually, the price matrix is positive on the diagonal (reward points) and negative
    elsewhere (price for mistake).

    This classifier uses `metrics.confusion_score` as its score. It is the score appropriate for
    evaluating the classification results, taking into account both `price` and `classprobs`.

    Parameters
    ----------
    classifier : linear classifier
        A linear classifier to wrap.

    price : array-like or DataFrame, optional
        Pricing matrix, with shape (n_classes_, n_classes_).
        If an array is given, the classes are inferred from the training labels, in sorted order.
        If not provided, the pricing matrix has 1 on the diagonal and -1 elsewhere.

    classprobs : array-like or Series, optional
        Probability of appearance for each class.
        If an array is given instead of a Series, classes are assumed in the same manner
        as for `price`. If not provided, assigns equal probabilities to all classes.
        Given probabilities will be normalized internally.

    n_jobs : int, default 1
        Number of jobs to run in parallel.
    """
    def __init__(
            self,
            classifier: LinearClassifier,
            *,
            price: ArrayLike | pd.DataFrame | None = None,
            classprobs: ArrayLike | pd.Series | None = None,
            n_jobs: int = 1,
    ):
        self.classifier = classifier
        self.price = price
        self.classprobs = classprobs
        self.n_jobs = n_jobs

    def score(self, X, y, sample_weight=None):
        return confusion_score(y, self.predict(X), self.price, classprobs=self.classprobs, sample_weight=sample_weight)

    def predict_proba(self, X):
        return self._predict_proba_lr(X)

    def predict_log_proba(self, X):
        return np.log(self.predict_proba(X))

    def _check_price(self) -> pd.DataFrame:
        if isinstance(self.price, pd.DataFrame):
            return self.price.reindex(index=self.classes_, columns=self.classes_, copy=False, fill_value=0)
        if self.price is None:
            price = pd.DataFrame(np.full((len(self.classes_),) * 2, -1), index=self.classes_, columns=self.classes_)
            diag = np.arange(price.shape[0])
            price.values[diag, diag] = 1
            return price
        price = np.asarray(self.price)
        if price.ndim != 2 or price.shape[0] != price.shape[1] or price.shape[0] != len(self.classes_):
            raise ValueError(
                f"`price` must be square and have length {len(self.classes_)}; got shape {price.shape}."
            )
        return pd.DataFrame(price, index=self.classes_, columns=self.classes_)

    def _check_classprobs(self) -> pd.Series:
        if isinstance(self.classprobs, pd.Series):
            probs = self.classprobs.reindex(self.classes_, copy=False, fill_value=0)
        elif self.classprobs is None:
            probs = pd.Series(1, index=self.classes_)
        else:
            probs = np.asarray(self.classprobs)
            if probs.ndim != 1 or len(probs) != len(self.classes_):
                raise ValueError(
                    "Dimensions mismatch for `classprobs`: "
                    f"Should be 1-dim with length {len(self.classprobs)}; got {probs.shape}."
                )
            probs = pd.Series(probs, index=self.classes_)
        return probs / probs.sum()

    def fit(self, X, y):
        X, y = self._validate_data(X, y, accept_sparse='csr', dtype=np.float64, order="C")
        self.classes_ = np.unique(y)
        price = self._check_price()
        probs = self._check_classprobs()
        fit_single = delayed(_fit_single_linear_ovr)
        fitted = Parallel(self.n_jobs)(
            fit_single(clone(self.classifier), X, y, price[c] * probs) for c in self.classes_
        )
        self.coef_ = np.empty((len(self.classes_), X.shape[1]), dtype=np.float64)
        self.intercept_ = np.empty(len(self.classes_), dtype=np.float64)
        for i, clf in enumerate(fitted):
            scale = price[self.classes_[i]].abs().mean()
            norm = np.linalg.norm(clf.coef_[0])
            if norm != 0:
                scale /= norm
            self.coef_[i] = clf.coef_[0] * scale
            self.intercept_[i] = clf.intercept_[0] * scale
        return self

def _fit_single_linear_ovr(clf: LinearClassifier, X: NDArray, y: NDArray, classweights: pd.Series) -> LinearClassifier:
    yy = np.where(np.in1d(y, classweights.index[classweights > 0]), 1, -1)
    w = classweights.abs()[y].values
    return clf.fit(X, yy, sample_weight=w)


import copy
import random
import tiles as tls
import judge as jdg

def draw_player(info_list):
    yama = info_list[0]
    tehai_a = info_list[1]
    kawa = info_list[2]
    turn = info_list[3]

    t = turn[0]
    tehai = tehai_a[t]
    tehai[0].append(yama.pop(0))

def draw_cpu(info_list):
    yama = info_list[0]
    tehai_a = info_list[1]
    kawa = info_list[2]
    turn = info_list[3]
    p_kaze = info_list[4]

    tehai = tehai_a[turn[0]]
    tehai[0].append(yama.pop(0))

    #tls.print_tehai(tehai, turn)

    i = random.randint(0,len(tehai[0]) - 1)
    #print(tls.hai_name(tehai[0][i]))

    if (p_kaze[0]+1)%4 == turn[0]:#playerの下家
        hai = tehai[0][i] + 0.3
    if (p_kaze[0]+2)%4 == turn[0]:#playerの対面
        hai = tehai[0][i] + 0.2
    if (p_kaze[0]+3)%4 == turn[0]:#playerの上家
        hai = tehai[0][i] + 0.4
    kawa[turn[0]].append(hai)

    #jdg.naki(yama, tehai_a, tehai[0][i], turn, kawa, p_kaze)
    tehai[0].pop(i)
    #jdg.ron(tehai_a, i, turn)


import sys
import requests
import json
from bs4 import BeautifulSoup

def main():

    # git link available in argv[1]
    git_link = sys.argv[1]

    try:
        response = requests.get(git_link)
        soup = BeautifulSoup(response.text, "html.parser")

        repo_name = soup.find(class_='repohead').find(
            'strong', attrs={"itemprop": "name"}).find('a')['href'][1:].split('/')

        star_count = soup.find(class_='pagehead-actions').find_all(
            'li')[1].find(class_="js-social-count")['aria-label'].split(' ')[0]

        response_obj = {
            'repository': repo_name,
            'star': star_count
        }
        sys.stdout.write(json.JSONEncoder().encode(response_obj))

    except:
        response_obj = {
            'error': "Invalid URL supplied"
        }
        sys.stderr.write(json.JSONEncoder().encode(response_obj))
        sys.exit(1)

if __name__ == "__main__":
    main()


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

class ICNN(nn.Module):
    """
		Input convex neural network (ICNN)
		input: y
		output: z = h_L
		Architecture: h_1     = LeakyReLU^2(A_0 y + b_0)
					  h_{l+1} = LeakyReLU  (A_l y + b_l + W_{l-1} h_l)
		Projection: W_l >= 0
	"""

    def __init__(self, input_dim=2, hidden_size=64, num_layers=4):
        super().__init__()
        self.num_layers = num_layers

        input_fcs = [
            nn.Linear(input_dim, hidden_size) for _ in range(num_layers)
        ]
        input_fcs.append(nn.Linear(input_dim, 1))

        hidden_fcs = [
            nn.Linear(hidden_size, hidden_size, bias=False) for _ in range(num_layers - 1)
        ]
        hidden_fcs.append(nn.Linear(hidden_size, 1, bias=False))

        self.input_fcs = nn.ModuleList(input_fcs)
        self.hidden_fcs = nn.ModuleList(hidden_fcs)

    @property
    def pos_weights(self):
        ws = []
        for m in self.hidden_fcs.modules():
            if isinstance(m, nn.Linear):
                ws.append(m.weight)
        return ws

    def project(self):
        for w in self.pos_weights:
            w.data.clamp_(min=0)

    def cvx_regularization(self):
        reg = 0.0
        for w in self.pos_weights:
            reg = reg + F.relu(w).square().sum()
        return reg

    def forward(self, y):
        h = F.leaky_relu(self.input_fcs[0](y), 0.2)
        h = h * h

        for y_fc, h_fc in zip(self.input_fcs[1:], self.hidden_fcs):
            h = F.leaky_relu(y_fc(y) + h_fc(h), 0.2)
        return h

if __name__ == "__main__":

    import matplotlib.pyplot as plt

    icnn = ICNN(input_dim=1)
    icnn.project()

    xx = torch.linspace(-10, 10, 1000).reshape(-1, 1)
    yy = icnn(xx)

    xx = xx.detach().numpy().reshape(-1)
    yy = yy.detach().numpy().reshape(-1)

    plt.plot(xx, yy)
    plt.savefig("icnn.png")