"""Constructs related to type annotations."""

import dataclasses
import logging
import typing
from typing import Mapping, Optional, Set, Tuple, Type, Union as _Union

from pytype import datatypes
from pytype.abstract import _base
from pytype.abstract import _classes
from pytype.abstract import _instance_base
from pytype.abstract import abstract_utils
from pytype.abstract import function
from pytype.abstract import mixin
from pytype.pytd import pytd_utils

log = logging.getLogger(__name__)

def _get_container_type_key(container):
  try:
    return container.get_type_key()
  except AttributeError:
    return container

class AnnotationClass(_instance_base.SimpleValue, mixin.HasSlots):
  """Base class of annotations that can be parameterized."""

  def __init__(self, name, ctx):
    super().__init__(name, ctx)
    mixin.HasSlots.init_mixin(self)
    self.set_native_slot("__getitem__", self.getitem_slot)

  def getitem_slot(self, node, slice_var):
    """Custom __getitem__ implementation."""
    slice_content = abstract_utils.maybe_extract_tuple(slice_var)
    inner, ellipses = self._build_inner(slice_content)
    value = self._build_value(node, tuple(inner), ellipses)
    return node, value.to_variable(node)

  def _build_inner(self, slice_content):
    """Build the list of parameters.

    Args:
      slice_content: The iterable of variables to extract parameters from.

    Returns:
      A tuple of a list of parameters and a set of indices at which an ellipsis
        was replaced with Any.
    """
    inner = []
    ellipses = set()
    for var in slice_content:
      if len(var.bindings) > 1:
        self.ctx.errorlog.ambiguous_annotation(self.ctx.vm.frames, var.data)
        inner.append(self.ctx.convert.unsolvable)
      else:
        val = var.bindings[0].data
        if val is self.ctx.convert.ellipsis:
          # Ellipses are allowed only in special cases, so turn them into Any
          # but record the indices so we can check if they're legal.
          ellipses.add(len(inner))
          inner.append(self.ctx.convert.unsolvable)
        else:
          inner.append(val)
    return inner, ellipses

  def _build_value(self, node, inner, ellipses):
    raise NotImplementedError(self.__class__.__name__)

  def __repr__(self):
    return f"AnnotationClass({self.name})"

  def _get_class(self):
    return self.ctx.convert.type_type

class AnnotationContainer(AnnotationClass):
  """Implementation of X[...] for annotations."""

  def __init__(self, name, ctx, base_cls):
    super().__init__(name, ctx)
    self.base_cls = base_cls

  def __repr__(self):
    return f"AnnotationContainer({self.name})"

  def _sub_annotation(
      self, annot: _base.BaseValue, subst: Mapping[str, _base.BaseValue],
      seen: Optional[Set[_base.BaseValue]] = None,
  ) -> _base.BaseValue:
    """Apply type parameter substitutions to an annotation."""
    # This is very similar to annotation_utils.sub_one_annotation, but a couple
    # differences make it more convenient to maintain two separate methods:
    # - subst here is a str->BaseValue mapping rather than str->Variable, and it
    #   would be wasteful to create variables just to match sub_one_annotation's
    #   expected input type.
    # - subst contains the type to be substituted in, not an instance of it.
    #   Again, instantiating the type just to later get the type of the instance
    #   is unnecessary extra work.
    if seen is None:
      seen = set()
    if annot in seen:
      return annot.ctx.convert.unsolvable
    seen = seen | {annot}
    if isinstance(annot, TypeParameter):
      if annot.full_name in subst:
        return subst[annot.full_name]
      else:
        return self.ctx.convert.unsolvable
    elif isinstance(annot, mixin.NestedAnnotation):
      inner_types = [(key, self._sub_annotation(val, subst, seen))
                     for key, val in annot.get_inner_types()]
      return annot.replace(inner_types)
    return annot

  def _get_value_info(
      self, inner, ellipses, allowed_ellipses=frozenset()
  ) -> Tuple[Tuple[_Union[int, str], ...], Tuple[_base.BaseValue, ...],
             Type[_classes.ParameterizedClass]]:
    """Get information about the container's inner values.

    Args:
      inner: The list of parameters from _build_inner().
      ellipses: The set of ellipsis indices from _build_inner().
      allowed_ellipses: Optionally, a set of indices at which ellipses are
        allowed. If omitted, ellipses are assumed to be never allowed.

    Returns:
      A tuple of the template, the parameters, and the container class.
    """
    if self.base_cls.full_name == "typing.Protocol":
      return abstract_utils.build_generic_template(inner, self) + (
          _classes.ParameterizedClass,)  # pytype: disable=bad-return-type
    if isinstance(self.base_cls, _classes.TupleClass):
      template = tuple(range(self.base_cls.tuple_length))
    elif isinstance(self.base_cls, _classes.CallableClass):
      template = tuple(range(self.base_cls.num_args)) + (abstract_utils.RET,)
    else:
      template = tuple(t.name for t in self.base_cls.template)
    self.ctx.errorlog.invalid_ellipses(self.ctx.vm.frames,
                                       ellipses - allowed_ellipses, self.name)
    last_index = len(inner) - 1
    if last_index and last_index in ellipses and len(inner) > len(template):
      # Even if an ellipsis is not allowed at this position, strip it off so
      # that we report only one error for something like 'List[int, ...]'
      inner = inner[:-1]
    if isinstance(self.base_cls, _classes.ParameterizedClass):
      # We're dealing with a generic type alias, e.g.:
      #   X = Dict[T, str]
      #   def f(x: X[int]): ...
      # We construct `inner` using both the new inner values and the ones
      # already in X, to end up with a final result of:
      #   template=(_K, _V)
      #   inner=(int, str)
      new_inner = []
      inner_idx = 0
      subst = {}
      # Note that we ignore any missing or extra values in inner for now; the
      # problem will be reported later by _validate_inner.
      for k in template:
        v = self.base_cls.formal_type_parameters[k]
        if v.formal:
          params = self.ctx.annotation_utils.get_type_parameters(v)
          for param in params:
            # If there are too few parameters, we ignore the problem for now;
            # it'll be reported when _build_value checks that the lengths of
            # template and inner match.
            if param.full_name not in subst and inner_idx < len(inner):
              subst[param.full_name] = inner[inner_idx]
              inner_idx += 1
          new_inner.append(self._sub_annotation(v, subst))
        else:
          new_inner.append(v)
      inner = tuple(new_inner)
      if isinstance(self.base_cls, _classes.TupleClass):
        template += (abstract_utils.T,)
        inner += (self.ctx.convert.merge_values(inner),)
      elif isinstance(self.base_cls, _classes.CallableClass):
        template = template[:-1] + (abstract_utils.ARGS,) + template[-1:]
        args = inner[:-1]
        inner = args + (self.ctx.convert.merge_values(args),) + inner[-1:]
      abstract_class = type(self.base_cls)
    else:
      abstract_class = _classes.ParameterizedClass
    return template, inner, abstract_class

  def _validate_inner(self, template, inner, raw_inner):
    """Check that the passed inner values are valid for the given template."""
    if (isinstance(self.base_cls, _classes.ParameterizedClass) and
        not abstract_utils.is_generic_protocol(self.base_cls)):
      # For a generic type alias, we check that the number of typevars in the
      # alias matches the number of raw parameters provided.
      template_length = raw_template_length = len(
          set(self.ctx.annotation_utils.get_type_parameters(self.base_cls)))
      inner_length = len(raw_inner)
      base_cls = self.base_cls.base_cls
    else:
      # In all other cases, we check that the final template length and
      # parameter count match, after any adjustments like flattening the inner
      # argument list in a Callable.
      template_length = len(template)
      raw_template_length = len(self.base_cls.template)
      inner_length = len(inner)
      base_cls = self.base_cls
    if inner_length != template_length:
      if not template:
        self.ctx.errorlog.not_indexable(
            self.ctx.vm.frames, base_cls.name, generic_warning=True)
      else:
        # Use the unprocessed values of `template` and `inner` so that the error
        # message matches what the user sees.
        if isinstance(self.base_cls, _classes.ParameterizedClass):
          error_template = None
        else:
          error_template = (t.name for t in base_cls.template)
        self.ctx.errorlog.wrong_annotation_parameter_count(
            self.ctx.vm.frames, self.base_cls, raw_inner, raw_template_length,
            error_template)
    else:
      if len(inner) == 1:
        val, = inner
        # It's a common mistake to index a container class rather than an
        # instance (e.g., list[0]).
        # We only check the "int" case, since string literals are allowed for
        # late annotations.
        if (isinstance(val, _instance_base.Instance) and
            val.cls == self.ctx.convert.int_type):
          # Don't report this error again.
          inner = (self.ctx.convert.unsolvable,)
          self.ctx.errorlog.not_indexable(self.ctx.vm.frames, self.name)
      # Check for a misused Final annotation
      if any(isinstance(val, FinalAnnotation) for val in inner):
        self.ctx.errorlog.invalid_final_type(self.ctx.vm.frames)
        inner = [val.annotation if isinstance(val, FinalAnnotation) else val
                 for val in inner]
    return inner

  def _build_value(self, node, inner, ellipses):
    if self.base_cls.is_late_annotation():
      # A parameterized LateAnnotation should be converted to another
      # LateAnnotation to delay evaluation until the first late annotation is
      # resolved. We don't want to create a ParameterizedClass immediately
      # because (1) ParameterizedClass expects its base_cls to be a
      # class_mixin.Class, and (2) we have to postpone error-checking anyway so
      # we might as well postpone the entire evaluation.
      printed_params = []
      added_typing_imports = set()
      for i, param in enumerate(inner):
        if i in ellipses:
          printed_params.append("...")
        else:
          typ = param.get_instance_type(node)
          annot, typing_imports = pytd_utils.MakeTypeAnnotation(typ)
          printed_params.append(annot)
          added_typing_imports.update(typing_imports)

      expr = f"{self.base_cls.expr}[{', '.join(printed_params)}]"
      annot = LateAnnotation(expr, self.base_cls.stack, self.ctx,
                             typing_imports=added_typing_imports)
      self.ctx.vm.late_annotations[self.base_cls.expr].append(annot)
      return annot
    template, processed_inner, abstract_class = self._get_value_info(
        inner, ellipses)
    if isinstance(self.base_cls, _classes.ParameterizedClass):
      base_cls = self.base_cls.base_cls
    else:
      base_cls = self.base_cls
    if base_cls.full_name in ("typing.Generic", "typing.Protocol"):
      # Generic is unique in that parameterizing it defines a new template;
      # usually, the parameterized class inherits the base class's template.
      # Protocol[T, ...] is a shorthand for Protocol, Generic[T, ...].
      template_params = [
          param.with_scope(base_cls.full_name)
          for param in typing.cast(Tuple[TypeParameter, ...], processed_inner)]
    else:
      template_params = None
    processed_inner = self._validate_inner(template, processed_inner, inner)
    params = {
        name: (processed_inner[i]
               if i < len(processed_inner) else self.ctx.convert.unsolvable)
        for i, name in enumerate(template)
    }

    # Check if the concrete types match the type parameters.
    if base_cls.template:
      processed_params = self.ctx.annotation_utils.convert_class_annotations(
          node, params)
      for formal_param in base_cls.template:
        root_node = self.ctx.root_node
        param_value = processed_params[formal_param.name]
        if (isinstance(formal_param, TypeParameter) and
            not formal_param.is_generic() and
            isinstance(param_value, TypeParameter)):
          if formal_param.name == param_value.name:
            # We don't need to check if a TypeParameter matches itself.
            continue
          else:
            actual = param_value.instantiate(
                root_node, container=abstract_utils.DUMMY_CONTAINER)
        elif param_value.is_concrete and isinstance(param_value.pyval, str):
          expr = param_value.pyval
          annot = LateAnnotation(expr, self.ctx.vm.frames, self.ctx)
          base = expr.split("[", 1)[0]
          self.ctx.vm.late_annotations[base].append(annot)
          actual = annot.instantiate(root_node)
        else:
          actual = param_value.instantiate(root_node)
        match_result = self.ctx.matcher(node).compute_one_match(
            actual, formal_param)
        if not match_result.success:
          if isinstance(param_value, TypeParameter):
            # bad_matches replaces type parameters in the expected type with
            # their concrete values, which is usually what we want. But when the
            # actual type is a type parameter, then it's more helpful to show
            # the expected type as a type parameter as well.
            bad = []
            for match in match_result.bad_matches:
              expected = dataclasses.replace(match.expected, typ=formal_param)
              bad.append(dataclasses.replace(match, expected=expected))
            if isinstance(formal_param, TypeParameter):
              details = (f"TypeVars {formal_param.name} and {param_value.name} "
                         "have incompatible bounds or constraints.")
            else:
              details = None
          else:
            bad = match_result.bad_matches
            details = None
          self.ctx.errorlog.bad_concrete_type(
              self.ctx.vm.frames, root_node, bad, details)
          return self.ctx.convert.unsolvable

    try:
      return abstract_class(base_cls, params, self.ctx, template_params)
    except abstract_utils.GenericTypeError as e:
      self.ctx.errorlog.invalid_annotation(self.ctx.vm.frames, e.annot, e.error)
      return self.ctx.convert.unsolvable

  def call(self, node, func, args, alias_map=None):
    return self._call_helper(node, self.base_cls, func, args)

class _TypeVariableInstance(_base.BaseValue):
  """An instance of a type parameter."""

  def __init__(self, param, instance, ctx):
    super().__init__(param.name, ctx)
    self.cls = self.param = param
    self.instance = instance
    self.scope = param.scope

  @property
  def full_name(self):
    return f"{self.scope}.{self.name}" if self.scope else self.name

  def call(self, node, func, args, alias_map=None):
    var = self.instance.get_instance_type_parameter(self.name)
    if var.bindings:
      return function.call_function(self.ctx, node, var, args)
    else:
      return node, self.ctx.convert.empty.to_variable(self.ctx.root_node)

  def __eq__(self, other):
    if isinstance(other, type(self)):
      return self.param == other.param and self.instance == other.instance
    return NotImplemented

  def __hash__(self):
    return hash((self.param, self.instance))

  def __repr__(self):
    return f"{self.__class__.__name__}({self.name!r})"

class TypeParameterInstance(_TypeVariableInstance):
  """An instance of a TypeVar type parameter."""

class ParamSpecInstance(_TypeVariableInstance):
  """An instance of a ParamSpec type parameter."""

class _TypeVariable(_base.BaseValue):
  """Parameter of a type."""

  formal = True

  _INSTANCE_CLASS: Type[_TypeVariableInstance] = None

  def __init__(self,
               name,
               ctx,
               constraints=(),
               bound=None,
               covariant=False,
               contravariant=False,
               scope=None):
    super().__init__(name, ctx)
    # TODO(b/217789659): PEP-612 does not mention constraints, but ParamSpecs
    # ignore all the extra parameters anyway..
    self.constraints = constraints
    self.bound = bound
    self.covariant = covariant
    self.contravariant = contravariant
    self.scope = scope

  @_base.BaseValue.module.setter
  def module(self, module):
    super(_TypeVariable, _TypeVariable).module.fset(self, module)
    self.scope = module

  @property
  def full_name(self):
    return f"{self.scope}.{self.name}" if self.scope else self.name

  def is_generic(self):
    return not self.constraints and not self.bound

  def copy(self):
    return self.__class__(self.name, self.ctx, self.constraints, self.bound,
                          self.covariant, self.contravariant, self.scope)

  def with_scope(self, scope):
    res = self.copy()
    res.scope = scope
    return res

  def __eq__(self, other):
    if isinstance(other, type(self)):
      return (self.name == other.name and
              self.constraints == other.constraints and
              self.bound == other.bound and
              self.covariant == other.covariant and
              self.contravariant == other.contravariant and
              self.scope == other.scope)
    return NotImplemented

  def __ne__(self, other):
    return not self == other

  def __hash__(self):
    return hash((self.name, self.constraints, self.bound, self.covariant,
                 self.contravariant))

  def __repr__(self):
    return ("{!s}({!r}, constraints={!r}, bound={!r}, module={!r})"
            .format(self.__class__.__name__, self.name, self.constraints,
                    self.bound, self.scope))

  def instantiate(self, node, container=None):
    var = self.ctx.program.NewVariable()
    if container and (not isinstance(container, _instance_base.SimpleValue) or
                      self.full_name in container.all_template_names):
      instance = self._INSTANCE_CLASS(self, container, self.ctx)  # pylint: disable=not-callable
      return instance.to_variable(node)
    else:
      for c in self.constraints:
        var.PasteVariable(c.instantiate(node, container))
      if self.bound:
        var.PasteVariable(self.bound.instantiate(node, container))
    if not var.bindings:
      var.AddBinding(self.ctx.convert.unsolvable, [], node)
    return var

  def update_official_name(self, name):
    if self.name != name:
      message = (f"TypeVar({self.name!r}) must be stored as {self.name!r}, "
                 f"not {name!r}")
      self.ctx.errorlog.invalid_typevar(self.ctx.vm.frames, message)

  def call(self, node, func, args, alias_map=None):
    return node, self.instantiate(node)

class TypeParameter(_TypeVariable):
  """Parameter of a type (typing.TypeVar)."""

  _INSTANCE_CLASS = TypeParameterInstance

class ParamSpec(_TypeVariable):
  """Parameter of a callable type (typing.ParamSpec)."""

  _INSTANCE_CLASS = ParamSpecInstance

class ParamSpecArgs(_base.BaseValue):
  """ParamSpec.args."""

  def __init__(self, paramspec, ctx):
    super().__init__(f"{paramspec.name}.args", ctx)
    self.paramspec = paramspec

  def instantiate(self, node, container=None):
    return self.to_variable(node)

class ParamSpecKwargs(_base.BaseValue):
  """ParamSpec.kwargs."""

  def __init__(self, paramspec, ctx):
    super().__init__(f"{paramspec.name}.kwargs", ctx)
    self.paramspec = paramspec

  def instantiate(self, node, container=None):
    return self.to_variable(node)

class Concatenate(_base.BaseValue):
  """Concatenation of args and ParamSpec."""

  def __init__(self, params, ctx):
    super().__init__("Concatenate", ctx)
    self.args = params[:-1]
    self.paramspec = params[-1]

  @property
  def full_name(self):
    return self.paramspec.full_name

  def instantiate(self, node, container=None):
    return self.to_variable(node)

  @property
  def num_args(self):
    return len(self.args)

  def get_args(self):
    # Satisfies the same interface as abstract.CallableClass
    return self.args

  def __repr__(self):
    args = ", ".join(list(map(repr, self.args)) + [self.paramspec.name])
    return f"Concatenate[{args}]"

class Union(_base.BaseValue, mixin.NestedAnnotation, mixin.HasSlots):
  """A list of types.

  Used for parameter matching.

  Attributes:
    options: Iterable of instances of BaseValue.
  """

  def __init__(self, options, ctx):
    super().__init__("Union", ctx)
    assert options
    self.options = list(options)
    self.cls = self._get_class()
    self._printing = False
    self._instance_cache = {}
    mixin.NestedAnnotation.init_mixin(self)
    mixin.HasSlots.init_mixin(self)
    self.set_native_slot("__getitem__", self.getitem_slot)

  def __repr__(self):
    if self._printing:  # recursion detected
      printed_contents = "..."
    else:
      self._printing = True
      printed_contents = ", ".join(repr(o) for o in self.options)
      self._printing = False
    return f"{self.name}[{printed_contents}]"

  def __eq__(self, other):
    if isinstance(other, type(self)):
      return self.options == other.options
    return NotImplemented

  def __ne__(self, other):
    return not self == other

  def __hash__(self):
    # Use the names of the parameter values to approximate a hash, to avoid
    # infinite recursion on recursive type annotations.
    return hash(tuple(o.full_name for o in self.options))

  def _unique_parameters(self):
    return [o.to_variable(self.ctx.root_node) for o in self.options]

  def _get_class(self):
    classes = {o.cls for o in self.options}
    if len(classes) > 1:
      return self.ctx.convert.unsolvable
    else:
      return classes.pop()

  def getitem_slot(self, node, slice_var):
    """Custom __getitem__ implementation."""
    slice_content = abstract_utils.maybe_extract_tuple(slice_var)
    params = self.ctx.annotation_utils.get_type_parameters(self)
    num_params = len({x.name for x in params})
    # Check that we are instantiating all the unbound type parameters
    if num_params != len(slice_content):
      self.ctx.errorlog.wrong_annotation_parameter_count(
          self.ctx.vm.frames, self, [v.data[0] for v in slice_content],
          num_params)
      return node, self.ctx.new_unsolvable(node)
    concrete = (
        var.data[0].instantiate(node, container=abstract_utils.DUMMY_CONTAINER)
        for var in slice_content)
    subst = datatypes.AliasingDict()
    for p in params:
      for k in subst:
        if k == p.name or k.endswith(f".{p.name}"):
          subst.add_alias(p.full_name, k)
          break
      else:
        subst[p.full_name] = next(concrete)
    new = self.ctx.annotation_utils.sub_one_annotation(node, self, [subst])
    return node, new.to_variable(node)

  def instantiate(self, node, container=None):
    var = self.ctx.program.NewVariable()
    for option in self.options:
      k = (node, _get_container_type_key(container), option)
      if k in self._instance_cache:
        if self._instance_cache[k] is None:
          self._instance_cache[k] = self.ctx.new_unsolvable(node)
        instance = self._instance_cache[k]
      else:
        self._instance_cache[k] = None
        instance = option.instantiate(node, container)
        self._instance_cache[k] = instance
      var.PasteVariable(instance, node)
    return var

  def call(self, node, func, args, alias_map=None):
    var = self.ctx.program.NewVariable(self.options, [], node)
    return function.call_function(self.ctx, node, var, args)

  def get_formal_type_parameter(self, t):
    new_options = [option.get_formal_type_parameter(t)
                   for option in self.options]
    return Union(new_options, self.ctx)

  def get_inner_types(self):
    return enumerate(self.options)

  def update_inner_type(self, key, typ):
    self.options[key] = typ

  def replace(self, inner_types):
    return self.__class__((v for _, v in sorted(inner_types)), self.ctx)

class LateAnnotation:
  """A late annotation.

  A late annotation stores a string expression and a snapshot of the VM stack at
  the point where the annotation was introduced. Once the expression is
  resolved, the annotation pretends to be the resolved type; before that, it
  pretends to be an unsolvable. This effect is achieved by delegating attribute
  lookup with __getattribute__.

  Note that for late annotation x, `isinstance(x, ...)` and `x.__class__` will
  use the type that x is pretending to be; `type(x)` will reveal x's true type.
  Use `x.is_late_annotation()` to check whether x is a late annotation.
  """

  _RESOLVING = object()

  def __init__(self, expr, stack, ctx, *, typing_imports=None):
    self.expr = expr
    self.stack = stack
    self.ctx = ctx
    self.resolved = False
    # Any new typing imports the annotation needs while resolving.
    self._typing_imports = typing_imports or set()
    self._type = ctx.convert.unsolvable  # the resolved type of `expr`
    self._unresolved_instances = set()
    self._resolved_instances = {}
    # _attribute_names needs to be defined last! This contains the names of all
    # of LateAnnotation's attributes, discovered by looking at
    # LateAnnotation.__dict__ and self.__dict__. These names are used in
    # __getattribute__ and __setattr__ to determine whether a given get/setattr
    # call should operate on the LateAnnotation itself or its resolved type.
    self._attribute_names = (
        set(LateAnnotation.__dict__) |
        set(super().__getattribute__("__dict__")))

  def flatten_expr(self):
    """Flattens the expression into a legal variable name if necessary.

    Pytype stores parameterized recursive types in intermediate variables. If
    self is such a type, this method flattens self.expr into a string that can
    serve as a variable name. For example, 'MyRecursiveAlias[int, str]' is
    flattened into '_MyRecursiveAlias_LBAR_int_COMMA_str_RBAR'.

    Returns:
      If self is a parameterized recursive type, a flattened version of
      self.expr that is a legal variable name. Otherwise, self.expr unchanged.
    """
    if "[" in self.expr and self.is_recursive():
      # _DOT and _RBAR have no trailing underscore because they precede names
      # that we already prefix an underscore to.
      return "_" + self.expr.replace(".", "_DOT").replace(
          "[", "_LBAR_").replace("]", "_RBAR").replace(", ", "_COMMA_")
    return self.expr

  def unflatten_expr(self):
    """Unflattens a flattened expression."""
    if "_LBAR_" in self.expr:
      mod, dot, rest = self.expr.rpartition(".")
      # The [1:] slicing and trailing underscore in _DOT_ are to get rid of
      # leading underscores added when flattening.
      return mod + dot + rest[1:].replace("_DOT_", ".").replace(
          "_LBAR_", "[").replace("_RBAR", "]").replace("_COMMA_", ", ")
    return self.expr

  def __repr__(self):
    return "LateAnnotation({!r}, resolved={!r})".format(
        self.expr, self._type if self.resolved else None)

  # __hash__ and __eq__ need to be explicitly defined for Python to use them in
  # set/dict comparisons.

  def __hash__(self):
    return hash(self._type) if self.resolved else hash(self.expr)

  def __eq__(self, other):
    return hash(self) == hash(other)

  def __getattribute__(self, name):
    # We use super().__getattribute__ directly for attribute access to avoid a
    # performance penalty from this function recursively calling itself.
    get = super().__getattribute__
    if name == "_attribute_names" or name in get("_attribute_names"):
      return get(name)
    return get("_type").__getattribute__(name)  # pytype: disable=attribute-error

  def __setattr__(self, name, value):
    if not hasattr(self, "_attribute_names") or name in self._attribute_names:
      return super().__setattr__(name, value)
    return self._type.__setattr__(name, value)

  def resolve(self, node, f_globals, f_locals):
    """Resolve the late annotation."""
    if self.resolved:
      return
    # Sets resolved to a truthy value distinguishable from True so that
    # 'if self.resolved' is True when self is partially resolved, but code that
    # really needs to tell partially and fully resolved apart can do so.
    self.resolved = LateAnnotation._RESOLVING
    # Add implicit imports for typing, since we can have late annotations like
    # `set[int]` which get converted to `typing.Set[int]`.
    if self._typing_imports:
      overlay = self.ctx.vm.import_module("typing", "typing", 0)
      for v in self._typing_imports:
        if v not in f_globals.members:
          f_globals.members[v] = overlay.get_module(v).load_lazy_attribute(v)
    var, errorlog = abstract_utils.eval_expr(self.ctx, node, f_globals,
                                             f_locals, self.expr)
    if errorlog:
      self.ctx.errorlog.copy_from(errorlog.errors, self.stack)
    self._type = self.ctx.annotation_utils.extract_annotation(
        node, var, None, self.stack)
    if self._type != self.ctx.convert.unsolvable:
      # We may have tried to call __init__ on instances of this annotation.
      # Since the annotation was unresolved at the time, we need to call
      # __init__ again to define any instance attributes.
      for instance in self._unresolved_instances:
        if isinstance(instance.cls, Union):
          # Having instance.cls be a Union type will crash in attribute.py.
          # Setting it to Any picks up the annotation in another code path.
          instance.cls = self.ctx.convert.unsolvable
        else:
          self.ctx.vm.reinitialize_if_initialized(node, instance)
    self.resolved = True
    log.info("Resolved late annotation %r to %r", self.expr, self._type)

  def set_type(self, typ):
    # Used by annotation_utils.sub_one_annotation to substitute values into
    # recursive aliases.
    assert not self.resolved
    self.resolved = True
    self._type = typ

  def to_variable(self, node):
    if self.resolved:
      return self._type.to_variable(node)
    else:
      return _base.BaseValue.to_variable(self, node)  # pytype: disable=wrong-arg-types

  def instantiate(self, node, container=None):
    """Instantiate the pointed-to class, or record a placeholder instance."""
    if self.resolved:
      key = (node, _get_container_type_key(container))
      if key not in self._resolved_instances:
        self._resolved_instances[key] = self._type.instantiate(node, container)
      return self._resolved_instances[key]
    else:
      instance = _instance_base.Instance(self, self.ctx)
      self._unresolved_instances.add(instance)
      return instance.to_variable(node)

  def get_special_attribute(self, node, name, valself):
    if name == "__getitem__" and not self.resolved:
      container = _base.BaseValue.to_annotation_container(self)  # pytype: disable=wrong-arg-types
      return container.get_special_attribute(node, name, valself)
    return self._type.get_special_attribute(node, name, valself)

  def is_late_annotation(self):
    return True

  def is_recursive(self):
    """Check whether this is a recursive type."""
    if not self.resolved:
      return False
    seen = {id(self)}
    stack = [self._type]
    while stack:
      t = stack.pop()
      if t.is_late_annotation():
        if id(t) in seen:
          return True
        seen.add(id(t))
      if isinstance(t, mixin.NestedAnnotation):
        stack.extend(child for _, child in t.get_inner_types())
    return False

class FinalAnnotation(_base.BaseValue):
  """Container for a Final annotation."""

  def __init__(self, annotation, ctx):
    super().__init__("FinalAnnotation", ctx)
    self.annotation = annotation

  def __repr__(self):
    return f"Final[{self.annotation}]"

  def instantiate(self, node, container=None):
    return self.to_variable(node)


class ArrayOperations:
    def getArray(self):
        global size
        arr=[]
        for i in range(size):
            print("Enter array number ",i+1)
            temp=[]
            for j in range(size):
                temp.append(int(input("Enter each element followed by the return key")))
            arr.append(temp)
        return arr
    def addArray(self,arr1,arr2):
        for i in range(size):
            global result
            resultTemp=[]
            for j in range(size):
                resultTemp.append(arr1[i][j]+arr2[i][j])
            result.append(resultTemp)
    def displayArray(self,res):
        global size
        for i in range(size):
            for x in res[i]:
                print(x,end=" ")
            print()

size=int(input("Enter the size of the array"))
obj=ArrayOperations()
arr1=obj.getArray()
arr2=obj.getArray()
result=[]
obj.addArray(arr1,arr2)
obj.displayArray(result)


from Components.config import config
from Components.ActionMap import ActionMap
from Components.Label import Label
from Components.Pixmap import Pixmap
from Plugins.Plugin import PluginDescriptor
from Screens.Screen import Screen

from Tools.Log import Log
from Tools.LoadPixmap import LoadPixmap
from Tools.Directories import resolveFilename, SCOPE_SKIN

class RemoteControlSelection(Screen):
	SKIN_DEFAULT = "skin_default"

	skin = """
		<screen name="RemoteControlSelection" position="center,80" size="420,610" title="RemoteControlSelection" >
			<widget name="rc" pixmap="skin_default/rc0.png" position="20,10" size="380,500" alphatest="on"/>
			<widget name="color_hint" position="10,520" size="400,50" font="Regular;18" halign="center" valign="center" backgroundColor="background" transparent="0" />
			<widget name="ok" position="10,580" size="400,24" font="Regular;22" halign="center" valign="center" backgroundColor="background" transparent="0" />
		</screen>
	"""

	def __init__(self, session):
		Screen.__init__(self, session)

		self["color_hint"] = Label(_("Some Remotes may exist in other Colors"))
		self["ok"] = Label(_("Press OK to apply"))

		self._pixmap = Pixmap()
		self["rc"] = self._pixmap

		self["actions"] = ActionMap(["DirectionActions", "OkCancelActions"],
			{
				"ok": self._ok,
				"cancel": self._cancel,
				"right": self._next,
				"left": self._prev,
			})

		self._pixmaps = []
		for i in (0, 1, 2, 3):
			self._pixmaps.append(
				LoadPixmap(
					resolveFilename(SCOPE_SKIN, "skin_default/rc%s.png" % (i))
				)
			)

		self._index = -1
		self.onFirstExecBegin.append(self._firstExecBegin)

	def _firstExecBegin(self):
		self.setTitle(_("Select your Remote"))
		self.setCurrentPixmap(config.misc.rcused.value)

	def _ok(self):
		config.misc.rcused.value = self._index
		config.misc.rcused.save()
		Log.i("RC is now set to Model %s" %(config.misc.rcused.value))
		self.close()

	def _cancel(self):
		self.close()

	def setCurrentPixmap(self, index):
		if index > 3:
			index = 0
		if index < 0:
			index = 3
		self._index = index
		self._pixmap.setPixmap(self._pixmaps[index])

	def _next(self):
		self._pixmap.setShowHideAnimation("slide_right_to_left")
		self.setCurrentPixmap(self._index + 1)

	def _prev(self):
		self._pixmap.setShowHideAnimation("slide_left_to_right")
		self.setCurrentPixmap(self._index - 1)

def remoteControlSelectionRun(session, **kwargs):
	session.open(RemoteControlSelection)

def remoteControlSelectionMenu(menuid, **kwargs):
	if menuid == "devices":
		return [(_("Remote Control Selection"), remoteControlSelectionRun, "rcu_selection", None)]
	else:
		return []

def Plugins(**kwargs):
	return PluginDescriptor(name=_("Remote Control Selection"), description=_("Select the remote you're using"), where=PluginDescriptor.WHERE_MENU, needsRestart=False, fnc=remoteControlSelectionMenu)


from typing import Optional, Tuple, Union

import paddle
import paddle.nn.functional as F

def cast_if_needed(tensor: Union[paddle.Tensor, None],
                   dtype: paddle.dtype) -> Union[paddle.Tensor, None]:
    """Cast tensor to dtype"""
    return tensor if tensor is None or tensor.dtype == dtype else paddle.cast(tensor, dtype)

def cast_if_needed_inplace(tensor: Union[paddle.Tensor, None],
                           dtype: paddle.dtype) -> Union[paddle.Tensor, None]:
    """Cast tensor to dtype (inplace), not to be used on layer inputs"""
    return tensor if tensor is None or tensor.dtype == dtype else tensor._to(dtype=dtype)

def check_dim_for_fp8_forward_exec(tensor: paddle.Tensor) -> bool:
    """For fp8 fprop (TN layout), inputs and weights must be such
       that dim0 is divisible by 8 and dim1 is divisible by 16.
    """
    return not tensor.shape[0] % 8 and not tensor.shape[1] % 16

def assert_dim_for_fp8_forward_exec(tensor: paddle.Tensor) -> None:
    """For fp8 fprop (TN layout), inputs and weights must be such
       that dim0 is divisible by 8 and dim1 is divisible by 16.
    """
    # single tensor check so it's clear which tensor is triggering the assertion
    assert check_dim_for_fp8_forward_exec(tensor), (
        "Tensor dimensions are not compatible for FP8 execution: "
        f"({tensor.shape[0]} % 8 != 0, {tensor.shape[1]} % 16 != 0)")

def get_bias_dtype(activation_dtype: paddle.dtype):
    """Get bias dtype given activation_dtype"""
    return paddle.bfloat16 if activation_dtype == paddle.float32 else activation_dtype

def get_paddle_act_func(activation):
    """Get paddle activation function"""
    funcs = {
        'gelu': F.gelu,
        'relu': F.relu,
    }
    if activation not in funcs:
        raise "Activation type " + activation + " is not supported."
    return funcs[activation]

def attention_mask_func(attention_scores: paddle.Tensor,
                        attention_mask: paddle.Tensor) -> paddle.Tensor:
    """Get attention mask"""

    def _masked_fill(x, mask, value):
        y = paddle.full(x.shape, value, x.dtype)
        return paddle.where(mask, y, x)

    attention_scores = _masked_fill(attention_scores, attention_mask, -10000.0)
    return attention_scores

def mask_to_cu_seqlens(mask: paddle.Tensor, need_kv: bool = False) -> paddle.Tensor:
    """Convert mask to cu_seqlens"""
    assert 'bool' in str(mask.dtype), "mask must be bool dtype"
    assert len(mask.shape) == 4 and mask.shape[1] == 1, "mask must be [b, 1, s_q, s_kv]"
    q_actual_seqlens = paddle.sum(mask[:, :, :, 0] == False, axis=(-1, -2), dtype='int32')    # pylint: disable=singleton-comparison
    q_cu_seqlens = paddle.cumsum(q_actual_seqlens)
    q_cu_seqlens = paddle.concat([paddle.zeros([1], dtype=paddle.int32), q_cu_seqlens], axis=0)
    if not need_kv:
        return q_cu_seqlens, None
    kv_actual_seqlens = paddle.sum(mask[:, :, 0, :] == False, axis=(-1, -2), dtype='int32')    # pylint: disable=singleton-comparison
    kv_cu_seqlens = paddle.cumsum(kv_actual_seqlens)
    kv_cu_seqlens = paddle.concat([paddle.zeros([1], dtype=paddle.int32), kv_cu_seqlens], axis=0)
    return q_cu_seqlens, kv_cu_seqlens

def divide(numerator: int, denominator: int) -> int:
    """Ensure that numerator is divisible by the denominator and return
    the division value."""
    assert (numerator % denominator == 0), f"{numerator} is not divisible by {denominator}"
    return numerator // denominator

def save_for_backward_allow_none(ctx, *args) -> None:
    """Save tensors for backward. Args could be None"""
    indices_mapping = []
    tensors_to_save = []
    for x in args:
        if isinstance(x, paddle.Tensor):
            indices_mapping.append(len(tensors_to_save))
            tensors_to_save.append(x)
        elif x is None:
            indices_mapping.append(-1)
        else:
            raise ValueError(f"Type {type(x)} is not allowed.")

    ctx._indices_mapping = indices_mapping
    ctx.save_for_backward(*tensors_to_save)

def saved_tensor_allow_none(ctx) -> Tuple[Optional[paddle.Tensor]]:
    """Used with `save_for_backward_allow_none` in pair. Get saved tensors from ctx."""
    assert hasattr(ctx, '_indices_mapping'), "`saved_tensor_allow_none` must be used " \
        "with `save_for_backward_allow_none` in pair."

    indices_mapping = ctx._indices_mapping
    outputs = []
    saved_tensors = ctx.saved_tensor()

    for index in indices_mapping:
        if index < 0:
            outputs.append(None)
        else:
            outputs.append(saved_tensors[index])

    return tuple(outputs)


# -*- coding: utf-8 -*-

import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
import pdb

def weight_variable(shape):
    "初始化权重"
    initial = tf.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    "初始化偏置项"
    initial = tf.constant(.1,shape=shape)
    return tf.Variable(initial)

def conv2d(x,W):
    "1步长,0边距的卷积"
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding="SAME")

def max_pool_2x2(x):
    "2x2的池化操作"
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")

if __name__ == "__main__":

    x = tf.placeholder("float", shape=[None, 784])
    y_ = tf.placeholder("float", shape=[None, 10])

    mnist = input_data.read_data_sets("MNIST_data",one_hot=True)
    sess = tf.InteractiveSession()

    # create CNN

    W_conv1 = weight_variable([5,5,1,32]) # 32个卷积核
    b_conv1 = bias_variable([32])

    x_image = tf.reshape(x,[-1,28,28,1])
    h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)
    h_pool1 = max_pool_2x2(h_conv1)

    W_conv2 = weight_variable([5,5,32,64]) # 64个卷积核
    b_conv2 = bias_variable([64])

    h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)
    h_pool2 = max_pool_2x2(h_conv2)

    W_fc1 = weight_variable([7*7*64,1024]) # 图片尺寸减小为 7x7， 输入channel为 64，输入总数为 7x7x64???? 前面的padding 用的same， 这里图片大小应该还是28x28??
    b_fc1 = bias_variable([1024])

    h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)

    keep_prob = tf.placeholder("float")
    h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)

    W_fc2 = weight_variable([1024,10])
    b_fc2 = bias_variable([10])

    y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)

    cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))
    accuarcy = tf.reduce_mean(tf.cast(correct_prediction,"float"))
    sess.run(tf.initialize_all_variables())

    for i in range(500):
        batch = mnist.train.next_batch(50)

        if i % 100 == 0:
            train_accuracy = accuarcy.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0})
            print("step %d, trainning accuarcy %g"%(i,train_accuracy))
        train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5})

    print("test accuarcy %g"%accuarcy.eval(feed_dict={
            x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))

    pdb.set_trace()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jun  5 20:17:37 2017

@author: afranio
"""

import numpy as np
import matplotlib.pyplot as plt

# dados experimentais
# benzeno - pressao de vapor X temperatura
P = np.array([    1,     5,    10,    20,    40,    60,   100,   200,   400,   760])   # mmHg
T = np.array([-36.7,  -19.6,  -11.5,   -2.6,   7.6,   15.4,   26.1, 42.2,   60.6,   80.1])     # C

# grau do polinomio a ser ajustado
n = 4

# ajustar o polinomio
c = np.polyfit(T,P,n)

# avaliando o polinomio para cada T (comparar com os dados experimentais)
z = np.polyval(c,T)

# dominio de T ''continuo'' (para plotar a correlacao)
Tc = np.arange(-37,81.1,0.1)

plt.plot(Tc,c[4]+c[3]*Tc+c[2]*Tc**2+c[1]*Tc**3+c[0]*Tc**4,'-',T,P,'*')

plt.title('Pressao de vapor benzeno - ajustada com polinomio de quarto grau')
plt.xlabel('T (C)')
plt.ylabel('P (mm Hg)')


import sys
import csv

csv_file_name = sys.argv[1] # Name of the file to parse
column_table_index = sys.argv[2] # The column which should be used as an index
column_table_index_base = int(sys.argv[3]) # The base of that column (i.e. base 16, base 10, base 2) expressed in base 10
column_table_value = sys.argv[4] # The column which should be used as a value
delimiter_ascii = str(chr(int(sys.argv[5]))) # The delimiter between each column
table_entry_count = int(sys.argv[6]) # Base 10 length of the array aligned to the nearest power of 2
value_data_type = str(sys.argv[7]) # The type of the array "int", "string", etc...

with open(csv_file_name, newline='') as csv_file:
        spam_reader = csv.reader(csv_file, delimiter=delimiter_ascii)

        idx_col_index = 0
        idx_col_value = 0

        for row in spam_reader:
                idx_col_index = row.index(column_table_index)
                idx_col_value = row.index(column_table_value)
                break

        row_count = 0

        a_table = [0] * table_entry_count

        print(column_table_index,":",column_table_value)
        for row in spam_reader:
                a_table[int(row[idx_col_index], base=column_table_index_base)] = row[idx_col_value]

                print(row[idx_col_index],":",row[idx_col_value])
                row_count += 1

        print(row_count,"rows")

        if (value_data_type == "int"):
                print("int array[] = { ")

                array_str = ""
                for i in range(table_entry_count):
                        array_str += str(a_table[i])
                        array_str += ','
                        if (i != table_entry_count - 1):
                               array_str += ' '

                print(array_str)
                print("};")


from unittest import TestCase
from set_matrix_zeroes import Solution

class TestSolution(TestCase):
    def test_set_zeroes(self):
        inputs = (
            [[1, 1, 1], [1, 0, 1], [1, 1, 1]],
            [[0, 1, 2, 0], [3, 4, 5, 2], [1, 3, 1, 5]]
        )
        outs = (
            [[1, 0, 1], [0, 0, 0], [1, 0, 1]],
            [[0, 0, 0, 0], [0, 4, 5, 0], [0, 3, 1, 0]]
        )
        for inp, out in zip(inputs, outs):
            Solution().setZeroes(inp)
            self.assertEqual(out, inp)


from PIL import ImageGrab,Image
import pytesseract

def yz_code():
    # bbox = (1348, 423, 1455, 455)  # 截图范围，这个取决你验证码的位置
    # img = ImageGrab.grab(bbox=bbox)
    # img.save("D:\\py\\login\\image_code.jpg")   # 设置路径
    # img.show()

    img = Image.open('img5.bmp')  # PIL库加载图片
    # print img.format, img.size, img.mode  # 打印图片信息
    img = img.convert('RGBA')  # 转换为RGBA
    pix = img.load()  # 读取为像素
    for x in range(img.size[0]):  # 处理上下黑边框
        pix[x, 0] = pix[x, img.size[1] - 1] = (255, 255, 255, 255)
    for y in range(img.size[1]):  # 处理左右黑边框
        pix[0, y] = pix[img.size[0] - 1, y] = (255, 255, 255, 255)
    for y in range(img.size[1]):  # 二值化处理，这个阈值为R=95，G=95，B=95
        for x in range(img.size[0]):
            if pix[x, y][0] < 95 or pix[x, y][1] < 95 or pix[x, y][2] < 95:
                pix[x, y] = (0, 0, 0, 255)
            else:
                pix[x, y] = (255, 255, 255, 255)

    img.save("img5.png")  # 由于tesseract限制，这里必须存到本地文件

    image_temp = Image.open('img5.png')

    signin_code = pytesseract.image_to_string(image_temp,lang='eng')

    print(signin_code)

yz_code()


import requests
import json
import numpy as np
import cv2
import os
from tqdm import tqdm

def crop_receipt(raw_img):
    """Crop receipt from a raw image captured by phone

    Args:
        raw_img ([np.array]): Raw image containing receipt

    Returns:
        cropped_receipt ([np.array]): The image of cropped receipt
    """

    CROP_RECEIPT_URL = 'http://service.aiclub.cs.uit.edu.vn/receipt/ript_detect'
    ROTATE_RECEIPT_URL = 'http://service.aiclub.cs.uit.edu.vn/receipt/ript_rotate90/'

    _, img_encoded = cv2.imencode('.jpg', raw_img)

    detect_receipt = requests.post(CROP_RECEIPT_URL, files={"file": (
        "filename", img_encoded.tostring(), "image/jpeg")}).json()
    receipt_box = detect_receipt['receipt']
    if receipt_box is not None:
        crop = raw_img[receipt_box[1]:receipt_box[3], receipt_box[0]:receipt_box[2]]
        img_crop_request = cv2.imencode('.jpg', crop)[1]
        files = [
            ('img', img_crop_request.tostring())
        ]
        rotated_func = requests.request("POST", "http://service.aiclub.cs.uit.edu.vn/receipt/ript_rotate90/", files=files).text
        rotated_func = rotated_func.split('\n')
        if rotated_func[0] != 'None' and float(rotated_func[1]) > 0.6:
            dic_rotate_fuc = {'ROTATE_90_CLOCKWISE':cv2.ROTATE_90_CLOCKWISE, 'ROTATE_90_COUNTERCLOCKWISE':cv2.ROTATE_90_COUNTERCLOCKWISE, 'ROTATE_180':cv2.ROTATE_180}
            crop = cv2.rotate(crop, dic_rotate_fuc[rotated_func[0]])
        return crop

    return raw_img


from tutelary.models import (
    PermissionSet, Policy, PolicyInstance
)
from django.contrib.auth.models import User
import pytest
from .factories import UserFactory, PolicyFactory
from .datadir import datadir  # noqa
from .settings import DEBUG

@pytest.fixture(scope="function")  # noqa
def setup(datadir, db):
    user1 = UserFactory.create(username='user1')
    user2 = UserFactory.create(username='user2')
    user3 = UserFactory.create(username='user3')

    PolicyFactory.set_directory(str(datadir))
    def_pol = PolicyFactory.create(name='def', file='default-policy.json')
    org_pol = PolicyFactory.create(name='org', file='org-policy.json')
    prj_pol = PolicyFactory.create(name='prj', file='project-policy.json')

    user1.assign_policies(def_pol)
    user2.assign_policies(def_pol,
                          (org_pol, {'organisation': 'Cadasta'}))
    user3.assign_policies(def_pol,
                          (org_pol, {'organisation': 'Cadasta'}),
                          (prj_pol, {'organisation': 'Cadasta',
                                     'project': 'TestProj'}))

    return (user1, user2, user3, def_pol, org_pol, prj_pol)

@pytest.fixture(scope="function")  # noqa
def debug(db):
    def fn(s):
        print(s)
        psets = PermissionSet.objects.all()
        print('PSets:', list(map(
            lambda pset: str(pset.pk) + ': ' + repr(pset.tree()),
            psets)
        ))
        pis = PolicyInstance.objects.all()
        print('PolInsts:', list(map(lambda pi:
                                    str(pi.pk) + ': ' + str(pi.pset.id) + ' ' +
                                    pi.policy.name + ' ' +
                                    str(pi.variables), pis)))

    def nofn(s):
        pass

    if DEBUG:
        return fn
    else:
        return nofn

def check(nuser=None, npol=None, npolin=None, npset=None):
    if nuser is not None:
        assert User.objects.count() == nuser
    if npol is not None:
        assert Policy.objects.count() == npol
    if npolin is not None:
        assert PolicyInstance.objects.count() == npolin
    if npset is not None:
        assert PermissionSet.objects.count() == npset

@pytest.mark.django_db  # noqa
def test_permission_set_creation(datadir, setup, debug):
    user1, user2, user3, def_pol, org_pol, prj_pol = setup

    debug('CREATION')
    check(nuser=3, npol=3, npolin=6, npset=3)

@pytest.mark.django_db  # noqa
def test_permission_set_change(datadir, setup, debug):
    user1, user2, user3, def_pol, org_pol, prj_pol = setup

    debug('BEFORE')
    user2.assign_policies(def_pol,
                          (org_pol, {'organisation': 'DummyCorp'}))
    debug('AFTER')

    check(nuser=3, npol=3, npolin=6, npset=3)

@pytest.mark.django_db  # noqa
def test_permission_set_clear_all(datadir, setup, debug):
    user1, user2, user3, def_pol, org_pol, prj_pol = setup

    debug('BEFORE')
    user1.assign_policies()
    user2.assign_policies()
    user3.assign_policies()
    debug('AFTER')

    # Remember the empty permission set!
    check(nuser=3, npol=3, npolin=0, npset=1)

@pytest.mark.django_db  # noqa
def test_permission_set_clear_single(datadir, setup, debug):
    user1, user2, user3, def_pol, org_pol, prj_pol = setup

    debug('BEFORE')
    user1.assign_policies()
    debug('AFTER')

    # Remember the empty permission set!
    check(nuser=3, npol=3, npolin=5, npset=3)

@pytest.mark.django_db  # noqa
def test_permission_user_deletion_single(datadir, setup, debug):
    user1, user2, user3, def_pol, org_pol, prj_pol = setup

    debug('BEFORE')
    user3.delete()
    debug('AFTER')

    # No empty permission set here: the user is gone!
    check(nuser=2, npol=3, npolin=3, npset=2)

@pytest.mark.django_db  # noqa
def test_permission_user_deletion_all(datadir, setup, debug):
    user1, user2, user3, def_pol, org_pol, prj_pol = setup

    debug('BEFORE')
    user1.delete()
    user2.delete()
    user3.delete()
    debug('AFTER')

    # No empty permission set here: the users are gone!
    check(nuser=0, npol=3, npolin=0, npset=0)


# -*- coding: utf-8 -*-
# @Author: Luis Condados
# @Date:   2023-09-09 18:46:06
# @Last Modified by:   Luis Condados
# @Last Modified time: 2023-09-16 18:33:43

import fiftyone as fo
import fiftyone.zoo as foz
import fiftyone.brain as fob
from sklearn.cluster import KMeans

import click

import logging

logging.basicConfig(level=logging.INFO)

def create_dataset_from_dir(images_dir, name=None, persistent=False):
    dataset = fo.Dataset.from_images_dir(images_dir=images_dir,
                                         name=name,
                                         persistent=persistent,
                                         recursive=True)

    return dataset

@click.command()
@click.option('--images_dir', '-i')
@click.option('--dataset_name', '--name', '-n')
@click.option('--persistent', '-p', type=bool, default=True, is_flag=True)
@click.option('--n_clusters', default=None, type=int)
def main(images_dir, dataset_name, persistent, n_clusters):

    if fo.dataset_exists(dataset_name):
        logging.info('Dataset {} already exists.'.format(dataset_name))
        dataset = fo.load_dataset(dataset_name)
    else:
        dataset = create_dataset_from_dir(images_dir, dataset_name)
        dataset.persistent = persistent

    ####################
    # Compute embeddings
    ####################

    logging.info('Computing embedding ...')
    #TIP: run foz.list_zoo_models() to see the whole list of models
    # model = foz.load_zoo_model("mobilenet-v2-imagenet-torch")
    model = foz.load_zoo_model('clip-vit-base32-torch')
    embeddings = dataset.compute_embeddings(model)

    logging.info('Working on the 2D for visualization ...')
    # Image embeddings
    fob.compute_visualization(dataset,
                              brain_key="latent_space",
                              embeddings=embeddings,
                              )

    # to enable the "search for similarity" feature
    fob.compute_similarity(dataset, embeddings=embeddings)

    ####################
    # K-means Clustering
    ####################
    if n_clusters != None:
        logging.info('Computing k-means clustering ...')
        k_means = KMeans(init="k-means++", n_clusters=n_clusters, n_init=10)
        k_means.fit(embeddings)

        for i, sample in enumerate(dataset):
            cluster_id = k_means.labels_[i]
            sample['cluster_id'] = cluster_id
            sample.save()

    ################
    # Launch the App
    ################
    session = fo.launch_app(dataset)

    # Blocks execution until the App is closed
    session.wait()

if __name__ == "__main__":
    main()


import math

def isPrime(n):
    i = 2
    while(i<=math.sqrt(n)):
        if(n%i==0):
            return False
        else:
            i += 1
    return True

def sieve(n):
    p_n = {}
    for i in range(2,n):
        p_n[i] = True
    for j in range(2,n):
        if p_n[j] == True:
            for a in range(2*j,n,j):
                p_n[a] = False
    return p_n

if __name__ == "__main__":
#    n = int(input("Enter number to check for primality: "))
#    if(isPrime(n)):
#        print("{} is a Prime Number.".format(n))
#    else:
#        print("{} is not a Prime Number.".format(n))
    test = sieve(2000000)
    n = int(input("Enter n < 2000000 to check for primality:"))
    print("{} is".format(n),end=" ")
    if not test[n]: print("not", end = " ")
    print("prime.")


# https://programmers.co.kr/learn/courses/30/lessons/42897

def solution(money):
    stole0_pprev = stole0_prev = money[0]
    stole1_pprev, stole1_prev = 0, money[1]
    for m in money[2:]:
        stole0_pprev, stole0_prev = stole0_prev, max(stole0_prev, stole0_pprev + m)
        stole1_pprev, stole1_prev = stole1_prev, max(stole1_prev, stole1_pprev + m)

    return max(stole0_pprev, stole1_prev)


"""....."""
brick_height = int(input('brick_height = '))
brick_width = int(input('brick_width = '))
brick_depth = int(input('brick_depth = '))
hole_height = int(input('hole_height = '))
hole_width = int(input('hole_width = '))

# Find two minimum dimensions of the hole
hole_min_0 = min(hole_width, hole_height)
hole_min_1 = max(hole_width, hole_height)

# Find two minimum dimensions of the brick
brick_max = max(brick_depth, brick_width, brick_height)
if brick_height == brick_max:
    x = brick_width
    y = brick_depth
elif brick_width == brick_max:
    x = brick_height
    y = brick_depth
else:
    x = brick_width
    y = brick_height
brick_min_0 = min(x, y)
brick_min_1 = max(x, y)

# Check if the brick can be passed into the hole
if brick_min_0 <= hole_min_0 and brick_min_1 <= hole_min_1:
    print("YES")
else:
    print("NO")

# brick_params = sorted([brick_width, brick_height, brick_depth])  # 2, 3, 4
# hole_params = sorted([hole_height, hole_width])  # 3, 2
#
# if brick_params[0] <= hole_params[0] and brick_params[1] <= hole_params[1]:
#     print('YES')
# else:
#     print('NO')


import pandas as pd
import numpy as np
import os
from datetime import timedelta
import math

pd.set_option('display.width', 1200)
pd.set_option('precision', 3)
np.set_printoptions(precision=3)
np.set_printoptions(threshold=np.nan)

class Config:
    __instance = None
    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args, **kwargs)
        return cls.__instance

    def __init__(self):
        print('Init Config!', os.getpid())

        # 文件路径相关参数
        self.rootPath = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0]
        self.ZZ800_DATA = self.rootPath + '/data/800_data.csv'
        self.ZZ800_RAW_DATA = self.rootPath + '/data/800_raw_data.csv'
        self.ZZ800_CODES = self.rootPath + '/data/800_codes.csv'
        self.ZZ800_RM_VR_FFT = self.rootPath + '/data/800_rm_vr_fft.csv'
        self.MARKET_RATIO = self.rootPath + '/data/index_ratio.csv'
        self.TRAINING_DAY = self.rootPath + '/data/trading_day.csv'

        # self.speed_method = 'value_ratio_fft_euclidean' # for 沪深300指数预测
        self.speed_method = 'rm_market_vr_fft' # for 沪深800选股

        self.update_start = '2018-05-18'  # 更新数据的开始时间(数据库日期的最后一天的下一天)
        self.update_end = '2018-05-21'  # 更新数据的结束时间(这一天也会被更新下来）

        self.auto_update = False # 回测时是否自动更新数据
        self.plot_simi_stock = False # 是否画出相似股票

        # self.is_regression_test = False  # 是回测还是预测
        # self.start_date = pd.to_datetime('2018-05-16') #回测的开始时间。 比如'2018-01-01'，则从'2018-01-02'开始做预测
        # self.regression_days = 5

        self.is_regression_test = True
        self.start_date = pd.to_datetime('2011-01-01')
        self.regression_days = 800

        self.regression_end_date = self.start_date + timedelta(days=self.regression_days) # 回测结束时间

        # 相似性查找参数
        self.pattern_length = 30
        self.nb_similar_make_prediction = 20  # avergae them as a pred
        self.nb_similar_of_all_similar = 4000  # 从所有股票的相似票中选择top N
        self.nb_similar_of_each_stock = 200

        self.slide_window = 1500

        self.weighted_dist = True
        self.weight_a = 1
        self.weight_b = 2
        self.alpha = np.multiply([1, 1, 1, 1, 1], 40)
        self.beata = np.multiply([1, 1, 1, 1, 1], math.pi / 180)

        self.fft_level = 5
        self.similarity_method = 'euclidean'  # or 'pearsonr'

        self.cores = 20
        self.nb_codes = 800

        # 输出文件地址
        name = str(self.start_date.date()) + '_' + str(self.speed_method) + '_' + str(self.nb_similar_make_prediction)
        self.PEARSON_CORR_RESLUT = self.rootPath + '/output/corr' + name + '.csv'
        self.PRDT_AND_ACT_RESULT = self.rootPath + '/output/pred' + name +'.csv'
        self.regression_result = self.rootPath + '/pic/para_' + name + '.png'

config = Config()

if __name__ == '__main__':
    std_data = pd.read_csv(config.DATA)


# Binary Tree

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

    def insert(self, data):
        # check if tree is null
        if self.data:
            # check 2 conditions: self.data > data or < data
            if self.data > data:
                if self.left is None:
                    self.left = Node(data)
                else:
                    self.left.insert(data)

            elif self.data < data:
                if self.right is None:
                    self.right = Node(data)
                else:
                    self.right.insert(data)

        else:
            self.data = data

    def print(self):
        if self.left:
            self.left.print()
        print(self.data)
        if self.right:
            self.right.print()

    # inorder
    def inorder(self, root):
        res = []
        if root:
            res = self.inorder(root.left)
            res.append(root.data)
            res += self.inorder(root.right)

        return res

    # preorder
    def preorder(self, root):
        res = []
        if root:
            res.append(root.data)
            res += self.preorder(root.left)
            res += self.preorder(root.right)

        return res

    # postorder
    def postorder(self, root):
        res = []
        if root:
            res = self.postorder(root.left)
            res += self.postorder(root.right)
            res.append(root.data)

        return res

    # Full Binary Tree: a node that has 0 or 2 children
    def isFullBinaryTree(self, root):
        if root is None:
            return True

        if root.left is None and root.right is None:
            return True

        if root.left is not None and root.right is not None:
            return self.isFullBinaryTree(root.left) and self.isFullBinaryTree(root.right)

        return False

    def depth(self, root):
        d = 0
        while root:
            d += 1
            root = root.left
        return d

    def bfs_tree(self, root, res):
        print('data: ',root.data)
        if root is None:
            return

        if root.left or root.right:
            if root.left:
                res.append(root.left.data)
            if root.right:
                res.append(root.right.data)
            if root.left:
                res = self.bfs_tree(root.left, res)
            if root.right:
                res = self.bfs_tree(root.right, res)

        return res

    # A Perfect Binary Tree: a tree that has both left and right children from every node from every level, and leaf nodes should be in the same level.
    # depth: O(ln(n)) where n is # of nodes
    # # of nodes: = 2^(h+1) - 1
    # leaf nodes: 2^h
    def isPerfectBinaryTree(self, root, d, level=0):
        # base case: if root is null
        if root is None:
            return True

        print('d: ', d)

        if root.left is None and root.right is None:
            return (d == level + 1)

        # only 1 child is None
        if root.left is None or root.right is None:
            return False

        return self.isPerfectBinaryTree(root.left, d, level + 1) and self.isPerfectBinaryTree(root.right, d, level + 1)

    # count # of nodes
    def countNodes(self, root):
        if root is None:
            return 0
        return 1 + self.countNodes(root.left) + self.countNodes(root.right)

    # Complete Binary Tree: all nodes leaning towards the left
    def isCompleteBinaryTree(self, root, index, num_nodes):
        if root is None:
            return True

        if index >= num_nodes:
            return False

        return self.isCompleteBinaryTree(root.left, 2 * index + 1, num_nodes) and self.isCompleteBinaryTree(root.right, 2 * index + 2, num_nodes)

    def isBalancedBinaryTree(self, root, height):
        if root is None:
            return True

        left_height = Height()
        right_height = Height()

        l = self.isBalancedBinaryTree(root.left, left_height)
        r = self.isBalancedBinaryTree(root.right, right_height)

        # update current root node's height
        height.height = max(left_height.height, right_height.height) + 1

        if abs(left_height.height - right_height.height) <= 1:
            return l and r

        return False

class Height:
    def __init__(self):
        self.height = 0

def MinimalTree(array):
    tree = Node(array[len(array) // 2])
    for element in array:
        tree.insert(element)

    return tree

# List of Depths - can do bfs
class LLNode:
    def __init__(self,data):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None

    def add(self, data):
        node = LLNode(data)
        node.next = self.head
        self.head = node

    def addToEnd(self, data):
        node = LLNode(data)

        if self.head is None:
            node.next = self.head
            self.head = node

        current = self.head
        while current.next:
            current = current.next

        node.next = current.next
        current.next = node

def createLevelLL(root, lists, level):
    if root is None:
        return

    linked_list = None
    if len(lists) == level: # if level is not contained in list
        linked_list = LinkedList()
        lists.append(linked_list)
    else:
        linked_list = lists[level]

    linked_list.add(root)
    createLevelLL(root.left, lists, level + 1)
    createLevelLL(root.right, lists, level + 1)

def list_of_depth(root):
    lists = []
    createLevelLL(root, lists, 0)
    return lists

array = [3,5,6,9,10,13]
minimal_tree = MinimalTree(array)
minimal_tree.print()
print(minimal_tree.preorder(minimal_tree))
print(minimal_tree.postorder(minimal_tree))
print(minimal_tree.inorder(minimal_tree))

# tree = Node(10)
# tree.insert(3)
# tree.insert(4)
# tree.insert(12)
# tree.insert(13)
# tree.print()
# print(tree.inorder(tree))
# print(tree.preorder(tree))
# print(tree.postorder(tree))
# print(tree.isFullBinaryTree(tree))

root = Node(2)
root.insert(1)
root.insert(3)
root.left.print()
root.right.print()
# root.left.left = Node(4)
# root.left.right = Node(5)
# root.right.left = Node(6)
# root.right.right = Node(7)

if root.isCompleteBinaryTree(root, 0, root.countNodes(root)):
    print('The tree is a complete binary tree')

if (root.isPerfectBinaryTree(root, root.depth(root))):
    print("The tree is a perfect binary tree")
else:
    print("The tree is not a perfect binary tree")

height = Height()

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
root.left.right.left = Node(6)
root.left.right.right = Node(7)
root.right.left = Node(8)
root.right.right = Node(9)
lists = list_of_depth(root)

for list in lists:
    current = list.head
    while current:
        print(current.data.data)
        current = current.next
    print()

if root.isBalancedBinaryTree(root, height):
    print('The tree is balanced')
else:
    print('The tree is not balanced')


""" The simple neural model definitions. """
from __future__ import absolute_import, division, print_function

import numpy as np
import tensorflow as tf

def neural_net(x, layers, keep_prob, weight_decay):
    y = tf.contrib.layers.flatten(x)
    embedding_layer = None
    for i, layer in enumerate(layers[:-1]):
        y = tf.contrib.layers.fully_connected(y, layer,
                                              weights_initializer=tf.contrib.layers.xavier_initializer(),
                                              weights_regularizer=tf.contrib.layers.l2_regularizer(weight_decay))
        embedding_layer = y
        y = tf.nn.relu(y)
    y = tf.nn.dropout(y, keep_prob=keep_prob)
    return tf.contrib.layers.fully_connected(y, layers[-1],
                                             weights_initializer=tf.contrib.layers.xavier_initializer(),
                                             weights_regularizer=tf.contrib.layers.l2_regularizer(weight_decay)), \
        embedding_layer

def conv_net(x, convs, fullys, keep_prob, weight_decay):
    y = x
    for conv in convs:
        y = tf.contrib.layers.conv2d(y, conv[0], kernel_size=[conv[1], conv[1]], stride=[1, 1], padding='SAME',
                                     weights_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                     weights_regularizer=tf.contrib.layers.l2_regularizer(weight_decay),
                                     activation_fn=tf.nn.relu)
        y = tf.contrib.layers.max_pool2d(y, kernel_size=[2, 2], padding='SAME')

    y = tf.reshape(y, [-1, np.prod(y.get_shape().as_list()[1:])])
    return neural_net(y, fullys, keep_prob, weight_decay)


def reversewords(s):
    container = []
    s = s.split(" ")
    z = ""
    for x in s:
        container.append(x)
    container = container[::-1]
    for x in range(0,len(container)):
        z += str(container[x])
        z += " "
    return z

print(reversewords("Hello this is max"))


"""
DjikistraAlgorithm
"""
import math
from random import randint

class DjikistraAlgorithm():
    """
    class DjikistraAlgorithm
    create minimum spanning tree path
    """
    def __init__(self):
        self.matrix=[]
        self.vertex=''
        self.num_vertices=self.input_vertex()
        self.create_matrix()

    def create_matrix(self):
        '''
        method - create adjacency matrix - represantation of graph
        '''
        self.matrix=[[randint(0,15) if row!=column else 0 for column in range(0,self.num_vertices)]for row in range(0,self.num_vertices)]
        #customization values in matrix
        for row in range(0,self.num_vertices):
            for column in range(0,self.num_vertices):
                self.matrix[column][row]=self.matrix[row][column]
        #if one vertices has not edges, create new matrix again
        for array in self.matrix:
            if any(array)<1:
                self.create_matrix()

    def display_matrix(self):
        """
        method - display matrix
        """
        for nums in self.matrix:
            print(nums)

    def input_vertex(self):
        """
        method - input number of vertices
        """
        while self.vertex not in range(0,10):
            self.vertex=int(input("Choose number of vertices in range[0,10]: "))
        return self.vertex

    def start_minimum_spanning_tree(self):
        """
        method - create minimum spanning tree
        """
        #create list of vertices, only first vertex is active
        selected_vector=[False if x!=0 else True for x in range(0,self.num_vertices)]
        weight=0
        connections_between_vertices=0
        #if there are less connections between vertices than number vertices-1,perform operations
        while connections_between_vertices<self.num_vertices-1:
            index_of_row_minimum_value=0
            index_of_column_minimum_value=0
            minimum=math.inf
            for row in range(0,self.num_vertices):
                if selected_vector[row]:
                    for column in range(0,self.num_vertices):
                        if (self.matrix[row][column]>0 and not selected_vector[column]):
                            if self.matrix[row][column]<minimum:
                                minimum=self.matrix[row][column]
                                index_of_row_minimum_value=row
                                index_of_column_minimum_value=column
            #if value of edges is chosen,we change the value on 0
            self.matrix[index_of_row_minimum_value][index_of_column_minimum_value]=0
            self.matrix[index_of_column_minimum_value][index_of_row_minimum_value]=0
            #added minimum value to weight
            weight+=minimum
            #we activate vector which is bound by edge which we selected
            selected_vector[index_of_column_minimum_value]=True
            #added minimum value to edges next vector which was selected
            for value in range(0,self.num_vertices):
                if self.matrix[index_of_column_minimum_value][value]!=0:
                    self.matrix[index_of_column_minimum_value][value]+=minimum
            print(f"Edge ({index_of_row_minimum_value}-{index_of_column_minimum_value}) and weight of edge={minimum}. All weight of edges={weight}")
            selected_vector[index_of_column_minimum_value]=True
            #added minimum value to weight vertices which are selected
            connections_between_vertices+=1
        print(f'Minimum spanning tree weight={weight}')

def __main__():
    djkistra=DjikistraAlgorithm()
    djkistra.display_matrix()
    djkistra.start_minimum_spanning_tree()

if __name__=="__main__":
    __main__()


# -*- coding: utf-8 -*-
"""
Created on Thu Jul 11 14:15:46 2019

@author: danie
"""

import geopandas as gpd
import pandas as pd
from shapely.geometry import LineString, Point
import os
import re
import numpy as np
import hkvsobekpy as his
import csv

#%%
def __between(value, a, b):
    # Find and validate before-part.
    pos_a = value.find(a)
    if pos_a == -1:
        return ""
    # Find and validate after part.
    pos_b = value.rfind(b)
    if pos_b == -1:
        return ""
    # Return middle part.
    adjusted_pos_a = pos_a + len(a)
    if adjusted_pos_a >= pos_b:
        return ""
    return value[adjusted_pos_a:pos_b]

def __split_line(lineString, point, buffer=False):
    if not buffer:
        pointIntersect = point
    else:
        pointIntersect = point.buffer(buffer)
    coords = lineString.coords
    j = None
    for i in range(len(coords) - 1):
        if LineString(coords[i : i + 2]).intersects(pointIntersect):
            j = i
            break
    assert j is not None
    # Make sure to always include the point in the first group
    return (
        coords[: j + 1] + [Point(point).coords[0]],
        [Point(point).coords[0]] + coords[j + 1 :],
    )

__friction_models = {
    "0": "chezy",
    "1": "manning",
    "2": "strickler (kn)",
    "3": "strickler (ks)",
    "4": "white-colebrook",
    "7": "bos and bijkerk",
}

__flow_boundary_types = {"0": "waterlevel", "1": "discharge"}

__structure_types = {"6": "weir", "7": "orifice", "9": "pump"}

__structure_flow_dirs = {"0": "both", "1": "positive", "2": "negative", "3": "no_flow"}

__pump_control = {"1": "suction", "2": "delivery", "3": "both_sides"}

__control_types = {"0": "time", "1": "hydraulic", "2": "interval", "3": "PID"}

__control_param = {
    "0": "crest_level",
    "1": "crest_width",
    "2": "gate_height",
    "3": "pump_capacity",
}

__profile_types = {}

__match_num = "[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?"

# %% read network
def network(path, crs):
    """Read al network-files."""
    links = gpd.GeoDataFrame(
        columns=["ID", "FROM_NODE", "TO_NODE", "geometry"], geometry="geometry"
    )
    nodes = gpd.GeoDataFrame(columns=["ID", "geometry"], geometry="geometry")

    with open(os.path.join(path, "network.tp"), "r") as networkTP:
        for line in networkTP.readlines():
            if line[0:4] == "NODE":
                ident = __between(line, "id '", "' nm")
                x = float(__between(line, "px ", " py"))
                y = float(__between(line, "py ", " node"))
                nodes = nodes.append(
                    {"ID": ident, "geometry": Point(x, y)}, ignore_index=True
                )
            elif line[0:4] == "BRCH":
                ident = __between(line, "id '", "' nm")
                from_node = __between(line, "bn '", "' en")
                to_node = __between(line, "en '", "' al")
                links = links.append(
                    {"ID": ident, "FROM_NODE": from_node, "TO_NODE": to_node},
                    ignore_index=True,
                )

    # open network.cp to define channel geometry
    with open(os.path.join(path, "network.cp"), "r") as networkCP:
        for reach in networkCP.read().split("BRCH")[1:]:
            ident = __between(reach, "id '", "' cp")
            cps = __between(reach, "TBLE\n", " <\ntble").split(" <\n")
            from_node = list(links.loc[links["ID"] == ident, "FROM_NODE"])[0]
            to_node = list(links.loc[links["ID"] == ident, "TO_NODE"])[0]
            coord_list = list(
                list(nodes.loc[nodes["ID"] == from_node].geometry)[0].coords
            )
            sumDistance = 0.0
            for idx, cp in enumerate(cps):
                distance, angle = cp.split()
                distance = (float(distance) - sumDistance) * 2
                angle = np.deg2rad(90 - float(angle))
                x = coord_list[-1][0] + float(distance) * np.cos(angle)
                y = coord_list[-1][1] + float(distance) * np.sin(angle)
                coord_list += [(x, y)]
                sumDistance += distance
            coord_list[-1] = list(
                list(nodes.loc[nodes["ID"] == to_node].geometry)[0].coords
            )[0]
            index = links.loc[links["ID"] == ident].index[0]
            links.at[index, "geometry"] = LineString(coord_list)

    network = {}
    objects = gpd.GeoDataFrame(
        columns=["ID", "TYPE", "LINK", "LINK_POS", "geometry"],
        geometry="geometry",
        crs=crs,
    )
    objects_list = []
    with open(os.path.join(path, "network.ntw"), "r") as networkNTW:
        doLinks = True
        for idx, l in enumerate(
            csv.reader(
                networkNTW.readlines(),
                quotechar='"',
                delimiter=",",
                quoting=csv.QUOTE_ALL,
            )
        ):
            if idx > 0:
                if doLinks:
                    if l[0] == "*":
                        doLinks = False
                if doLinks:
                    network.update(
                        {
                            l[0]: {
                                "properties": {
                                    "type": l[4],
                                    "customType": l[5],
                                    "startNode": l[14],
                                    "endNode": l[27],
                                },
                                "lineString": [
                                    [float(l[21]), float(l[22])],
                                    [float(l[34]), float(l[35])],
                                ],
                            }
                        }
                    )
                    if not l[14] in objects_list:
                        objects_list.append(l[14])
                        objects = objects.append(
                            {
                                "ID": l[14],
                                "NAME": l[15],
                                "TYPE": l[19],
                                "geometry": Point([float(l[21]), float(l[22])]),
                            },
                            ignore_index=True,
                        )
                    if not l[27] in objects_list:
                        objects_list.append(l[27])
                        objects = objects.append(
                            {
                                "ID": l[27],
                                "NAME": l[28],
                                "TYPE": l[32],
                                "geometry": Point([float(l[34]), float(l[35])]),
                            },
                            ignore_index=True,
                        )

    h_points = gpd.GeoDataFrame(
        columns=["ID", "geometry"], geometry="geometry", crs=crs
    )
    v_links = gpd.GeoDataFrame(
        columns=["ID", "TYPE", "CUSTOM_TYPE", "FROM_NODE", "TO_NODE", "geometry"],
        geometry="geometry",
        crs=crs,
    )

    with open(os.path.join(path, "network.gr"), "r") as networkGR:
        hLocations = his.read_his.ReadMetadata(
            os.path.join(path, "calcpnt.his"), hia_file="auto"
        ).GetLocations()
        for reach in networkGR.read().split("GRID")[1:]:
            ident = __between(reach, "id '", "' ci")
            line = list(links.loc[links["ID"] == ident, "geometry"])[0]
            gridTable = __between(reach, "TBLE\n", " <\ntble").split(" <\n")
            for idx, grid in enumerate(gridTable):
                grid = grid.split()
                h_point = grid[3].replace("'", "")
                if h_point in hLocations:  # check if point is ignored by Sobek-core
                    point = (float(grid[5]), float(grid[6]))
                    if h_point not in list(h_points["ID"]):
                        h_points = h_points.append(
                            {"ID": h_point, "geometry": Point(point)}, ignore_index=True
                        )
                    if idx == 0:
                        v_point = grid[4].replace("'", "")
                        Type = network[v_point]["properties"]["type"]
                        customType = network[v_point]["properties"]["customType"]
                        pointFrom = h_point
                    else:
                        pointTo = h_point
                        segment, line = __split_line(
                            LineString(line), Point(point), buffer=0.01
                        )
                        v_links = v_links.append(
                            {
                                "ID": v_point,
                                "TYPE": Type,
                                "CUSTOM_TYPE": customType,
                                "FROM_NODE": pointFrom,
                                "TO_NODE": pointTo,
                                "geometry": LineString(segment),
                            },
                            ignore_index=True,
                        )
                        v_point = grid[4].replace("'", "")
                        pointFrom = h_point

    # use ID as index
    for df in [links, nodes, objects, v_links]:
        df.index = df["ID"]

    with open(os.path.join(path, "network.cr"), "r") as networkCR:
        for line in networkCR:
            if re.match("CRSN", line):
                object_id = re.search(".id '(.*)' nm.", line).group(1)
                objects.loc[object_id, "LINK"] = re.search(".ci '(.*)' lc", line).group(
                    1
                )
                objects.loc[object_id, "LINK_POS"] = float(
                    re.search(".lc (.*) crsn", line).group(1)
                )

    with open(os.path.join(path, "network.st"), "r") as networkST:
        for line in networkST:
            if re.match("STRU", line):
                object_id = re.search(".id '(.*)' nm.", line).group(1)
                objects.loc[object_id, "LINK"] = re.search(".ci '(.*)' lc", line).group(
                    1
                )
                objects.loc[object_id, "LINK_POS"] = float(
                    re.search(".lc (.*) stru", line).group(1)
                )

    with open(os.path.join(path, "network.cn"), "r") as networkCN:
        for line in networkCN:
            if re.match("FLBX", line):
                object_id = re.search(".id '(.*)' nm.", line).group(1)
                objects.loc[object_id, "LINK"] = re.search(".ci '(.*)' lc", line).group(
                    1
                )
                objects.loc[object_id, "LINK_POS"] = float(
                    re.search(".lc (.*) flbx", line).group(1)
                )

    return {
        "links": links.set_crs(crs, inplace=True),
        "nodes": nodes.set_crs(crs, inplace=True),
        "objects": objects.set_crs(crs, inplace=True),
        "segments": v_links.set_crs(crs, inplace=True),
    }

def results(path):
    """Read meta-data from his-files."""
    files = {
        "links": "reachseg.his",
        "points": "calcpnt.his",
        "structures": "struc.his",
    }
    result = {"links": None, "points": None, "structures": None}
    for key, item in files.items():
        if os.path.exists(os.path.join(path, item)):
            meta_data = his.read_his.ReadMetadata(
                os.path.join(path, item), hia_file="auto"
            )
            parameters = meta_data.GetParameters()
            locations = meta_data.GetLocations()
            result.update(
                {
                    key: {
                        "df": meta_data.DataFrame(),
                        "parameters": parameters,
                        "locations": locations,
                    }
                }
            )

    return result

def parameters(path):
    """Read parameters from a sobek case."""
    result = dict()
    with path.joinpath("friction.dat").open() as friction_dat:
        result["friction"] = dict()
        for line in friction_dat:
            if re.match(".*BDFR.*", line):
                model = __friction_models[__between(line, 'mf', ' mt').replace(' ', '')]
                value = float(__between(line, 'mt cp 0', '0 mr').replace(' ', ''))
                result['friction']['global'] = {'model': model,
                                                'value': value}

    with path.joinpath('struct.dat').open() as struct_dat:
        structures = dict()
        for line in struct_dat:
            if re.match("STRU", line):
                struc_id = re.search(".id '(.*)' nm.", line).group(1)
                structures[struc_id] = {}
                structures[struc_id]["def_id"] = re.search(
                    ".dd '(.*)' ca.", line
                ).group(1)
                structures[struc_id]["control_id"] = re.search(
                    "cj '(.*)' ", line
                ).group(1)
                structures[struc_id]["control_active"] = bool(
                    int(re.search(f"ca ({__match_num}) ", line).group(1))
                )
        result["structures"] = structures

    with path.joinpath("struct.def").open() as struct_def:
        for stds in struct_def.read().split("stds"):
            if "STDS" in stds:
                def_id = re.search(".id '(.*)' nm.", stds).group(1)
                struc_def = dict()
                struc_def["type"] = __structure_types[
                    re.search(".ty ([0-9]).", stds).group(1)
                ]
                if struc_def["type"] in ["weir", "orifice"]:
                    struc_def["crest_level"] = float(
                        re.search(f".cl ({__match_num}).", stds).group(1)
                    )
                    struc_def["crest_width"] = float(
                        re.search(f".cw ({__match_num}).", stds).group(1)
                    )
                    struc_def["flow_dir"] = __structure_flow_dirs[
                        re.search(f".rt ({__match_num}).", stds).group(1)
                    ]
                    if struc_def["type"] == "weir":
                        cw = float(re.search(f".sc ({__match_num}).", stds).group(1))
                        ce = float(re.search(f".ce ({__match_num}).", stds).group(1))
                        struc_def["coefficient"] = ce * cw

                    if struc_def["type"] == "orifice":
                        cw = float(re.search(f".sc ({__match_num}).", stds).group(1))
                        mu = float(re.search(f".mu ({__match_num}).", stds).group(1))
                        struc_def["coefficient"] = mu * cw
                elif struc_def["type"] == "pump":
                    struc_def["control_side"] = __pump_control[
                        re.search(f".dn ({__match_num}).", stds).group(1)
                    ]
                    stages = (
                        re.search(".*\nTBLE\n(.*)<\ntble.", stds).group(1).split("<")
                    )
                    stages = [stage.split() for stage in stages]
                    struc_def["pump_stages"] = [
                        {
                            "capacity": float(stage[0]),
                            "suction_on": float(stage[1]),
                            "suction_off": float(stage[2]),
                            "delivery_on": float(stage[3]),
                            "delivery_off": float(stage[4]),
                        }
                        for stage in stages
                    ]

                struc_id = next(
                    (
                        st_id
                        for st_id, values in structures.items()
                        if values["def_id"] == def_id
                    ),
                    None,
                )
                if struc_id:
                    result["structures"][struc_id] = {
                        **result["structures"][struc_id],
                        **struc_def,
                    }
                else:
                    print(f"structure definition {def_id} not linked to structure-id")

        with path.joinpath("profile.dat").open() as profile_dat:
            cross_sections = dict()
            for line in profile_dat:
                if re.match("CRSN", line):
                    xs_id = re.search(".id '(.*)' di.", line).group(1)
                    cross_sections[xs_id] = re.search(".di '(.*)' rl.", line).group(1)
            result["cross_sections"] = cross_sections.copy()

        with path.joinpath("profile.def").open() as profile_dat:
            for crds in profile_dat.read().split("crds"):
                if "CRDS" in crds:
                    def_id = re.search(".id '(.*)' nm.", crds).group(1)
                    xs_type = re.search(f".ty ({__match_num}).", crds).group(1)
                    crds = crds.replace("\n", "")
                    coords = re.search(r".*TBLE(.*)<tble.", crds).group(1).split("<")
                    if xs_type == "0":
                        z = np.array([float(coord.split()[0]) for coord in coords])
                        w = np.array([float(coord.split()[1]) for coord in coords])
                        series = pd.Series(
                            data=np.concatenate([np.flip(z), z]),
                            index=np.concatenate([np.flip(-w / 2), w / 2]),
                        )
                    else:
                        print(f"ERROR: structure type {xs_type} not supported!")

                prof_ids = [
                    xs_id
                    for xs_id, xs_def in cross_sections.items()
                    if xs_def == def_id
                ]
                if prof_ids:
                    for prof_id in prof_ids:
                        result["cross_sections"][prof_id] = series.copy()
                else:
                    print(f"profile definition {def_id} not linked to profile-id")

    return result

def control(path):
    """Read controls from a sobek case."""
    result = dict()
    with path.joinpath("control.def").open() as control_def:
        for cntl in control_def.read().split("cntl"):
            if "CNTL" in cntl:
                cntl_def = {}
                def_id = re.search(".id '(.*)' nm.", cntl).group(1)
                cntl_def["type"] = __control_types[
                    re.search(f".ct ({__match_num}).", cntl).group(1)
                ]
                cntl_def["parameter"] = __control_param[
                    re.search(f".ca ({__match_num}).", cntl).group(1)
                ]
                if cntl_def["type"] == "PID":
                    cntl_def["min_value"] = float(
                        re.search(f".ui ({__match_num}) ", cntl).group(1)
                    )
                    cntl_def["max_value"] = float(
                        re.search(f".ua ({__match_num}) ", cntl).group(1)
                    )
                elif cntl_def["type"] == "time":
                    crest_levels = []
                    for cntl_line in cntl.splitlines():
                        if "<" in cntl_line:
                            crest_levels.append(float(cntl_line.split(" ")[1]))
                    if len(crest_levels) > 0:
                        cntl_def["min_value"] = np.min(crest_levels)
                        cntl_def["max_value"] = np.max(crest_levels)
                tble_str = cntl.replace("\n", "")
                if "TBLE" in tble_str:
                    cntl_def["table"] = {}
                    tbl_props = re.findall("PDIN (.*) pdin", tble_str)
                    if len(tbl_props) > 0:
                        tbl_props = tbl_props[0].split()
                        cntl_def["table"]["function"] = tbl_props[0]
                        cntl_def["table"]["use_periodicity"] = bool(int(tbl_props[1]))
                        if cntl_def["table"]["use_periodicity"] == "1":
                            cntl_def["table"]["periodicity"] = tbl_props[2]
                    tble_list = (
                        re.search(r".*TBLE(.*)<tble.", tble_str).group(1).split("<")
                    )
                    date_time = [
                        pd.to_datetime(row.split()[0], format="'%Y/%m/%d;%H:%M:%S'")
                        for row in tble_list
                    ]
                    values = [float(row.split()[1]) for row in tble_list]
                    cntl_def["table"]["data"] = pd.Series(data=values, index=date_time)
                result[def_id] = cntl_def

    return result

def boundaries(path):
    """Read boundaries from a sobek case."""
    result = dict()
    with path.joinpath("boundary.dat").open() as boundary_dat:
        result["flow"] = dict()
        for line in boundary_dat:
            if re.match("FLBO", line):
                ident = __between(line, "id", "st").replace(" ", "").replace("'", "")
                result["flow"][ident] = {
                    "TYPE": __flow_boundary_types[
                        re.search(".ty ([0-9]).", line).group(1)
                    ]
                }
        result["flow"] = pd.DataFrame.from_dict(result["flow"], orient="index")

    return result