#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import argparse
import json
import logging
import os
import shutil
import subprocess
import sys
import tempfile
import traceback
from pathlib import Path
from typing import Optional, Any, List

from pyre_extensions import none_throws, safe_json

try:
    from ..facebook.shim import configuration
except Exception:
    # pyre-ignore
    from . import configuration

import pyredex

LOG: logging.Logger = logging.getLogger(__name__)

class ClientError(Exception):
    pass

def _path_exists(path: str) -> str:
    path = os.path.expanduser(path)
    if not os.path.exists(path):
        raise argparse.ArgumentTypeError(f"Path `{path}` does not exist.")
    return os.path.realpath(path)

def _directory_exists(path: str) -> str:
    path = os.path.expanduser(path)
    if not os.path.isdir(path):
        raise argparse.ArgumentTypeError(f"Path `{path}` is not a directory.")
    path = os.path.realpath(path)
    if path and path[-1] != "/":
        path = path + "/"
    return path

def _separated_paths_exist(paths: Optional[str]) -> Optional[str]:
    if paths is None:
        return None

    elements = paths.split(";")
    return ";".join([_path_exists(element) for element in elements])

def _check_executable(path: Path) -> Path:
    if not (path.exists() and os.access(path, os.X_OK)):
        raise ClientError(f"Invalid binary `{path}`.")
    return path

def _system_jar_configuration_path(input: str) -> str:
    if input.endswith(".json"):
        path = _path_exists(input)
        with open(path) as file:
            try:
                paths = safe_json.load(file, List[str])
                return ";".join(paths)
            except safe_json.InvalidJson:
                raise argparse.ArgumentTypeError(
                    f"`{path} must contain a list of strings"
                )

    # Validation deferred to backend if we pass `;` separated list of paths
    # because they are allowed to not exist.
    return input

class ExtractJexException(ClientError):
    pass

def _extract_jex_file_if_exists(path: Path, target: str, build_directory: Path) -> Path:
    jex_extract_directory: Path = Path(build_directory) / "jex"

    def run_unzip_command(command: List[str]) -> Path:
        output = subprocess.run(command, stderr=subprocess.PIPE)
        if output.returncode != 0:
            stderr = output.stderr.decode()
            if (
                command[0] == "unzip"
                and output.returncode == 1
                and "extra bytes at beginning or within zipfile" in stderr
            ):
                # This warning is fine to ignore, because *.jar files have been properly extracted
                LOG.warning(f"`unzip` warning: {stderr}")
            else:
                command_string = " ".join(command)
                raise ExtractJexException(
                    f"Unable to extract binary file `{path}` with command `{command_string}`:\n{stderr}"
                )

        jar_file_path = jex_extract_directory / (
            target.rsplit(":", maxsplit=1)[1] + ".jar"
        )
        if jar_file_path.exists():
            return jar_file_path
        else:
            raise ClientError(
                f"Could not find jar file `{path}` in `{jex_extract_directory}`."
            )

    # If the target is java_binary, then the output is a JEX file
    if path.suffix != ".jex":
        return path

    # Try to extract *.jex files with various unzip tools, until one of them succeeds. See D22579374 for why doing this
    try:
        return run_unzip_command(
            ["unsquashfs", "-d", str(jex_extract_directory), "-o", "4096", str(path)]
        )
    except ExtractJexException:
        LOG.warning(f"Running `unsquashfs` on file `{path}` failed.")
        LOG.warning(f"Trying to extract file `{path}` with `unzip`.")
        return run_unzip_command(["unzip", "-d", str(jex_extract_directory), str(path)])

def _build_target(target: str, *, mode: Optional[str] = None) -> Path:
    LOG.info(f"Building `{target}`%s...", f" with `{mode}`" if mode else "")

    # If a target starts with fbcode, then it needs to be built from fbcode instead of from fbsource
    if ":" not in target:
        LOG.warning(
            f"Target `{target}` is an alias. Please expand it if it is a fbcode target. Otherwise buck build will fail."
        )
    fbcode_target_prefix = "fbcode//"
    is_fbcode_target = target.startswith(fbcode_target_prefix)
    if is_fbcode_target:
        target = target[len(fbcode_target_prefix) :]

    command = ["buck", "build", "--show-json-output"]
    if mode:
        command.append(str(mode))
    command.append(target)
    current_working_directory = Path(os.getcwd())
    working_directory = (
        current_working_directory / "fbcode"
        if is_fbcode_target
        else current_working_directory
    )
    output = subprocess.run(command, stdout=subprocess.PIPE, cwd=working_directory)
    if output.returncode != 0:
        raise ClientError(f"Error while building buck target `{target}`, aborting.")

    try:
        response = json.loads(output.stdout)
    except json.JSONDecodeError:
        response = {}

    if len(response) != 1 or len(next(iter(response.values()))) == 0:
        raise ClientError(f"Unexpected buck output:\n{output.stdout.decode()}")

    return working_directory / next(iter(response.values()))

def _build_executable_target(target: str, *, mode: Optional[str] = None) -> Path:
    return _check_executable(_build_target(target, mode=mode))

def _get_analysis_binary(arguments: argparse.Namespace) -> Path:
    from_arguments = arguments.binary
    if from_arguments:
        # Use the user-provided binary.
        return _check_executable(Path(from_arguments))

    buck_target = configuration.BINARY_BUCK_TARGET
    if buck_target:
        # Build the mariana-trench binary from buck (facebook-only).
        return _build_executable_target(
            buck_target,
            mode=arguments.build,
        )

    path_command = configuration.BINARY_PATH_COMMAND
    if path_command:
        # Find the mariana-trench binary in the path (open-source).
        command = shutil.which(path_command)
        if command is None:
            raise ClientError(f"Could not find `{path_command}` in PATH.")
        return Path(command)

    raise ClientError("Could not find the analyzer binary.")

def _desugar_jar_file(jar_path: Path) -> Path:
    LOG.info(f"Desugaring `{jar_path}`...")
    desugar_tool = _build_target(none_throws(configuration.DESUGAR_BUCK_TARGET))
    desugared_jar_file = jar_path.parent / (jar_path.stem + "-desugared.jar")
    output = subprocess.run(
        [
            "java",
            f"-Dlog4j.configurationFile={configuration.DESUGAR_LOG_CONFIGURATION_PATH}",
            "-jar",
            desugar_tool,
            os.fspath(jar_path),
            os.fspath(desugared_jar_file),
        ]
    )
    if output.returncode != 0:
        raise ClientError("Error while desugaring jar file, aborting.")

    LOG.info(f"Desugared jar file: `{desugared_jar_file}`.")
    return desugared_jar_file

def _build_apk_from_jar(jar_path: Path) -> Path:
    _, dex_file = tempfile.mkstemp(suffix=".jar")

    LOG.info(f"Running d8 on `{jar_path}`...")
    output = subprocess.run(
        [
            "/opt/android/sdk_D23134735/build-tools/29.0.2/d8",
            "-JXmx8G",
            jar_path,
            "--output",
            dex_file,
            "--lib",
            "/opt/android/sdk_D23134735/platforms/android-29/android.jar",
            "--min-api",
            "25",  # mininum api 25 corresponds to dex 37
        ]
    )
    if output.returncode != 0:
        raise ClientError("Error while running d8, aborting.")

    return Path(dex_file)

class VersionAction(argparse.Action):
    def __call__(self, parser: argparse.ArgumentParser, *_: Any) -> None:
        from . import package

        print(f"{package.name} {package.version}")
        print("Copyright (c) Facebook, Inc. and its affiliates.")
        parser.exit()

def main() -> None:
    logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
    build_directory = Path(tempfile.mkdtemp())
    try:
        parser = argparse.ArgumentParser(
            description="A security-focused static analyzer targeting Android."
        )
        parser.add_argument(
            "--version",
            action=VersionAction,
            nargs=0,
            help="Print the version and exit",
        )

        target_arguments = parser.add_argument_group("Target arguments")
        target_arguments.add_argument(
            "--apk-path",
            type=_path_exists,
            help="The APK to analyze.",
        )
        if configuration.FACEBOOK_SHIM:
            target_arguments.add_argument(
                "--java-target",
                type=str,
                help="The java buck target to analyze. If the target is `java_library`, append `-javaX` were X is the java version (e.g. 11).",
            )
            target_arguments.add_argument(
                "--java-mode",
                type=str,
                help="The buck mode for building the java target.",
            )

        output_arguments = parser.add_argument_group("Output arguments")
        output_arguments.add_argument(
            "--output-directory",
            type=_directory_exists,
            default=".",
            help="The directory to store results in.",
        )
        output_arguments.add_argument(
            "--generated-models-directory",
            type=_directory_exists,
            help="Save generated models to this directory.",
        )

        binary_arguments = parser.add_argument_group("Analysis binary arguments")
        binary_arguments.add_argument(
            "--binary", type=str, help="The Mariana Trench binary."
        )
        if configuration.FACEBOOK_SHIM:
            binary_arguments.add_argument(
                "--build",
                type=str,
                default=none_throws(configuration.BINARY_BUCK_BUILD_MODE),
                metavar="BUILD_MODE",
                help="The Mariana Trench binary buck build mode.",
            )

        configuration_arguments = parser.add_argument_group("Configuration arguments")
        configuration_arguments.add_argument(
            "--system-jar-configuration-path",
            type=_system_jar_configuration_path,
            default=os.fspath(configuration.get_path("default_system_jar_paths.json")),
            help="A JSON configuration file with a list of paths to the system jars "
            + "or a `;` separated list of jars.",
        )
        configuration_arguments.add_argument(
            "--rules-paths",
            type=str,
            default=os.fspath(configuration.get_path("rules.json")),
            help="A `;`-separated list of rules files and directories containing rules files.",
        )
        configuration_arguments.add_argument(
            "--repository-root-directory",
            type=_directory_exists,
            default=".",
            help="The root of the repository. Resulting paths will be relative to this.",
        )
        configuration_arguments.add_argument(
            "--source-root-directory",
            type=_directory_exists,
            default=".",
            help="The root where source files for the APK can be found.",
        )
        configuration_arguments.add_argument(
            "--source-exclude-directories",
            type=str,
            default=None,
            help="A `;`-separated list of directories that should be excluded from indexed source files.",
        )
        configuration_arguments.add_argument(
            "--proguard-configuration-paths",
            type=_separated_paths_exist,
            default=None,
            help="A `;`-separated list of ProGuard configurations, which can be used for global inference and to ignore unreachable objects.",
        )
        configuration_arguments.add_argument(
            "--remove-unreachable-code",
            action="store_true",
            help="Prune unreachable code based on entry points specified in proguard configuration.",
        )
        configuration_arguments.add_argument(
            "--lifecycles-paths",
            type=_separated_paths_exist,
            default=None,
            help="A `;`-separated list of files and directories containing lifecycle definitions.",
        )
        configuration_arguments.add_argument(
            "--model-generator-configuration-paths",
            type=_separated_paths_exist,
            default=os.fspath(configuration.get_path("default_generator_config.json")),
            help="""A `;`-separated list of paths specifying JSON configuration files. Each file is a list of paths
            to JSON model generators relative to the configuration file or names of CPP model generators.""",
        )
        configuration_arguments.add_argument(
            "--model-generator-search-paths",
            type=_separated_paths_exist,
            default=";".join(
                os.fspath(path)
                for path in configuration.get_default_generator_search_paths()
            ),
            help="A `;`-separated list of paths where we look up JSON model generators.",
        )
        configuration_arguments.add_argument(
            "--maximum-source-sink-distance",
            type=int,
            default=configuration.DEFAULT_MAXIMUM_SOURCE_SINK_DISTANCE,
            help="Limits the distance of sources and sinks from a trace entry point.",
        )

        analysis_arguments = parser.add_argument_group("Analysis arguments")
        analysis_arguments.add_argument(
            "--sequential",
            action="store_true",
            help="Run the analysis sequentially, one a single thread.",
        )
        analysis_arguments.add_argument(
            "--skip-source-indexing",
            action="store_true",
            help="Skip indexing source files.",
        )
        analysis_arguments.add_argument(
            "--skip-model-generation",
            action="store_true",
            help="Skip model generation.",
        )
        analysis_arguments.add_argument(
            "--disable-parameter-type-overrides",
            action="store_true",
            help="Disable analyzing methods with specific parameter type information.",
        )
        analysis_arguments.add_argument(
            "--maximum-method-analysis-time",
            type=int,
            help="Specify number of seconds as a bound. If the analysis of a method takes longer than this then make the method obscure (default taint-in-taint-out).",
        )

        debug_arguments = parser.add_argument_group("Debugging arguments")
        debug_arguments.add_argument(
            "--verbosity",
            type=int,
            default=1,
            metavar="[1-5]",
            help="The logging verbosity.",
        )
        debug_arguments.add_argument(
            "--gdb", action="store_true", help="Run the analyzer inside gdb."
        )
        debug_arguments.add_argument(
            "--lldb", action="store_true", help="Run the analyzer inside lldb."
        )
        debug_arguments.add_argument(
            "--log-method",
            action="append",
            metavar="PATTERN",
            help="Enable logging for the given methods.",
        )
        debug_arguments.add_argument(
            "--dump-class-hierarchies",
            action="store_true",
            help="Dump the class hierarchies in `class_hierarchies.json`.",
        )
        debug_arguments.add_argument(
            "--dump-overrides",
            action="store_true",
            help="Dump the override graph in `overrides.json`.",
        )
        debug_arguments.add_argument(
            "--dump-call-graph",
            action="store_true",
            help="Dump the call graph in `call_graph.json`.",
        )
        debug_arguments.add_argument(
            "--dump-dependencies",
            action="store_true",
            help="Dump the dependency graph in `dependencies.json`.",
        )
        debug_arguments.add_argument(
            "--dump-methods",
            action="store_true",
            help="Dump a list of the method signatures in `methods.json`.",
        )

        arguments: argparse.Namespace = parser.parse_args()

        if (
            configuration.FACEBOOK_SHIM
            and arguments.java_target is not None
            and arguments.apk_path is not None
        ):
            parser.error(
                "The analysis target can either be a java target (--java-target)"
                + " or an apk file (--apk-path), but not both."
            )
        if (
            configuration.FACEBOOK_SHIM
            and arguments.java_target is None
            and arguments.apk_path is None
        ):
            parser.error(
                "The analysis target should either be a java target (--java-target)"
                + " or an apk file (--apk-path)."
            )
        if not configuration.FACEBOOK_SHIM and arguments.apk_path is None:
            parser.error("The argument --apk-path is required.")

        # Build the vanilla java project.
        if configuration.FACEBOOK_SHIM and arguments.java_target:
            jar_file = _extract_jex_file_if_exists(
                _build_target(arguments.java_target, mode=arguments.java_mode),
                arguments.java_target,
                build_directory,
            )
            desugared_jar_file = _desugar_jar_file(jar_file)
            arguments.apk_path = os.fspath(_build_apk_from_jar(desugared_jar_file))

        # Build the mariana trench binary if necessary.
        binary = _get_analysis_binary(arguments)

        LOG.info(f"Extracting `{arguments.apk_path}`...")
        apk_directory = tempfile.mkdtemp(suffix="_apk")
        dex_directory = tempfile.mkdtemp(suffix="_dex")
        pyredex.utils.unzip_apk(arguments.apk_path, apk_directory)
        dex_mode = pyredex.unpacker.detect_secondary_dex_mode(apk_directory)
        dex_mode.unpackage(apk_directory, dex_directory)
        LOG.info(f"Extracted APK into `{apk_directory}` and DEX into `{dex_directory}`")

        options = [
            "--system-jar-paths",
            arguments.system_jar_configuration_path,
            "--apk-directory",
            apk_directory,
            "--dex-directory",
            dex_directory,
            "--rules-paths",
            arguments.rules_paths,
            "--repository-root-directory",
            arguments.repository_root_directory,
            "--source-root-directory",
            arguments.source_root_directory,
            "--apk-path",
            arguments.apk_path,
            "--output-directory",
            arguments.output_directory,
            "--maximum-source-sink-distance",
            str(arguments.maximum_source_sink_distance),
            "--model-generator-configuration-paths",
            arguments.model_generator_configuration_paths,
        ]

        if arguments.model_generator_search_paths is not None:
            options.append("--model-generator-search-paths")
            options.append(arguments.model_generator_search_paths)

        if arguments.proguard_configuration_paths:
            options.append("--proguard-configuration-paths")
            options.append(arguments.proguard_configuration_paths)

        if arguments.lifecycles_paths:
            options.append("--lifecycles-paths")
            options.append(arguments.lifecycles_paths)

        if arguments.source_exclude_directories:
            options.append("--source-exclude-directories")
            options.append(arguments.source_exclude_directories)

        if arguments.generated_models_directory:
            options.append("--generated-models-directory")
            options.append(arguments.generated_models_directory)

        if arguments.sequential:
            options.append("--sequential")
        if arguments.skip_source_indexing:
            options.append("--skip-source-indexing")
        if arguments.skip_model_generation:
            options.append("--skip-model-generation")
        if arguments.disable_parameter_type_overrides:
            options.append("--disable-parameter-type-overrides")
        if arguments.remove_unreachable_code:
            options.append("--remove-unreachable-code")
        if arguments.maximum_method_analysis_time is not None:
            options.append("--maximum-method-analysis-time")
            options.append(str(arguments.maximum_method_analysis_time))

        trace_settings = [f"MARIANA_TRENCH:{arguments.verbosity}"]
        if "TRACE" in os.environ:
            trace_settings.insert(0, os.environ["TRACE"])
        os.environ["TRACE"] = ",".join(trace_settings)

        if arguments.log_method:
            for method in arguments.log_method:
                options.append("--log-method=%s" % method.strip())
        if arguments.dump_class_hierarchies:
            options.append("--dump-class-hierarchies")
        if arguments.dump_overrides:
            options.append("--dump-overrides")
        if arguments.dump_call_graph:
            options.append("--dump-call-graph")
        if arguments.dump_dependencies:
            options.append("--dump-dependencies")
        if arguments.dump_methods:
            options.append("--dump-methods")

        command = [os.fspath(binary.resolve())] + options
        if arguments.gdb:
            command = ["gdb", "--args"] + command
        elif arguments.lldb:
            command = ["lldb", "--"] + command
        LOG.info(f"Running Mariana Trench: {' '.join(command)}")
        output = subprocess.run(command)
        if output.returncode != 0:
            LOG.fatal(f"Analysis binary exited with exit code {output.returncode}.")
            sys.exit(output.returncode)
    except (ClientError, configuration.Error) as error:
        LOG.fatal(error.args[0])
        sys.exit(1)
    except Exception:
        LOG.fatal(f"Unexpected error:\n{traceback.format_exc()}")
        sys.exit(1)
    finally:
        try:
            shutil.rmtree(build_directory)
        except IOError:
            pass  # Swallow.

if __name__ == "__main__":
    main()


"""
You are given two non-empty linked lists representing two non-negative integers.
The digits are stored in reverse order and each of their nodes contain a single
digit. Add the two numbers and return it as a linked list.

You may assume the two numbers do not contain any leading zero, except the
number 0 itself.

Example:

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
"""
import doctest

# Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        >>> s = Solution()

        Examples:
            >>> l1 = ListNode(2)
            >>> l1.next = ListNode(4)
            >>> l1.next.next = ListNode(3)
            >>> l2 = ListNode(5)
            >>> l2.next = ListNode(6)
            >>> l2.next.next = ListNode(4)
            >>> l = s.addTwoNumbers(l1, l2)
            >>> while l:
            ...     print(l.val, end='')
            ...     l = l.next
            708
            >>> l1 = ListNode(1)
            >>> l1.next = ListNode(8)
            >>> l2 = ListNode(0)
            >>> l = s.addTwoNumbers(l1, l2)
            >>> while l:
            ...     print(l.val, end='')
            ...     l = l.next
            18
        """
        head = ListNode(0)
        tmp = head
        carry = 0
        p, q = l1, l2
        while p or q:
            x, y = p.val if (p is not None) else 0, q.val if (q is not None) else 0
            new_node = ListNode(sum([x, y, carry]) % 10)
            carry = sum([x, y, carry]) // 10
            tmp.next = new_node
            tmp = new_node
            p = p.next if p else None
            q = q.next if q else None
        if carry:
            new_node = ListNode(carry)
            tmp.next = new_node
        return head.next

if __name__ == '__main__':
    doctest.testmod(verbose=True)


#Crie um algoritimo que leia um nº e mostre seu dobro, triplo e raiz quadrada
nome = input('Falai, qual teu nome? ')
print('Hello word e hello {}, prazer parça!' .format(nome))
n1 = int(input('Agora me diga um número: '))
dobro = n1 * 2
triplo = n1 * 3
rq = n1 ** (1/2)
print('{} o dobro de {} vale {}, já o triplo é {} e a raiz quadrada é {:.3f}' .format( nome, n1, dobro, triplo, rq) ,end=' ')
print('Se bola que aki sou eu ..|.. ')


# I made this in python and ran it through json.dumps() to get the json
# and then I changed a whole lot of the values
# so this doesn't match that at all, really, sorry

# below is an example search
# NOTE: for any item you don't want to search -- say you don't care about fiscal year --
# 	you can put in "False" or just leave it out.
#   Also, if you only want to specify a start date/year OR an end date/year, leave out
#   the one you don't care about. Just, erase it. It's fine.

search = {
	"fiscal_year": {"begin": 2010, "end": 2019},
	"start_date": {"begin": "2015-01-01", "end": "2018-09-01"},
	"area": {'Engineering and Construction': True,
		    'Administration/Sub-Award': True,
		    'Facility Improvement': True,
		    'Neighborhood and Community Development': True,
		    '': True
		    },
	"asset_type": { 'Bridge': False,
				    'Facility': False,
				    'Park': False,
				    'Playground': False,
				    'Playing Field': False,
				    'Pool': False,
				    'Signalized Intersection': False,
				    'Step': False,
				    'Retaining Wall': False,
				    'Non-Asset': False,
				    'Roofing System': False,
				    'Sidewalk': False,
				    'Court': False
		      },
	"status": {'Planned': False,
			   'Completed': True,
			   'In Progress': True,
			   'Canceled': True
			  }
}


import os
import numpy as np
from timefreq import tools

NOISE_RATIO = 0.0005   # fixme 0.0005

DURATION = 0.125   # [s]
FS = 8000           # [Hz]
N_SAMPLES = int(DURATION * FS) + 1
MIN_FREQ = 40       # [Hz]
MAX_FREQ = 600     # [Hz]
MIN_FREQ_TEST = MIN_FREQ + 1
MAX_FREQ_TEST = MAX_FREQ - 10
MIN_ANALYSIS_FREQ = 20
MAX_ANALYSIS_FREQ = 3700

MIN_HARMONICS = 5
TWO_PI = 2 * np.pi

A4_FREQ = 440
log_min_interval_1_32_semitone = 1.0 / (12 * 32)  # fixme  4 -> 32
log2_a4_freq = np.log2(A4_FREQ)
log2_min_freq = np.log2(MIN_FREQ)
log2_max_freq = np.log2(MAX_FREQ)
min_log_freq = log2_a4_freq \
               - int((log2_a4_freq - log2_min_freq) / log_min_interval_1_32_semitone) * log_min_interval_1_32_semitone
max_log_freq = log2_a4_freq \
               + int((log2_max_freq - log2_a4_freq) / log_min_interval_1_32_semitone) * log_min_interval_1_32_semitone
n_tones = (max_log_freq - min_log_freq) / log_min_interval_1_32_semitone
n_tones = int(n_tones) + 1
FUNDAMENTAL_FREQS = np.power(2.0, np.linspace(min_log_freq, max_log_freq, n_tones))

log2_min_freq = np.log2(MIN_ANALYSIS_FREQ)
log2_max_freq = np.log2(MAX_ANALYSIS_FREQ)
min_log_freq = log2_a4_freq \
               - int((log2_a4_freq - log2_min_freq) / log_min_interval_1_32_semitone) * log_min_interval_1_32_semitone
max_log_freq = log2_a4_freq \
               + int((log2_max_freq - log2_a4_freq) / log_min_interval_1_32_semitone) * log_min_interval_1_32_semitone
n_tones = (max_log_freq - min_log_freq) / log_min_interval_1_32_semitone
n_tones = int(n_tones) + 1
ANALYSIS_FREQS = np.power(2.0, np.linspace(min_log_freq, max_log_freq, n_tones))

SEED = 0
TRAIN_SIZE = 20000

N_CLASSES = len(FUNDAMENTAL_FREQS)

def generate_signal(freq, random_state, noise_ratio=None):
    signal_x = np.linspace(0, DURATION, N_SAMPLES)

    current_harmonic = freq
    possible_harmonics = []
    while current_harmonic < FS / 2:
        possible_harmonics.append(current_harmonic)
        current_harmonic += freq

    n_harmonics = random_state.randint(MIN_HARMONICS, len(possible_harmonics))
    chosen_harmonics = random_state.choice(possible_harmonics, n_harmonics, replace=False)

    signal_y = np.zeros(len(signal_x))
    for current_freq in chosen_harmonics:
        if noise_ratio is not None:
            current_freq = current_freq \
                           - current_freq * noise_ratio / 2.0 \
                           + random_state.rand() * current_freq * noise_ratio

        start_phase = random_state.rand() * TWO_PI
        signal_y += np.sin((TWO_PI * current_freq) * signal_x + start_phase)

    return signal_y

def create_test_sample(random_state=None, noise_ratio=None):
    if random_state is None:
        random_state = np.random.RandomState()
    min_log_freq = np.log2(MIN_FREQ_TEST)
    max_log_freq = np.log2(MAX_FREQ_TEST)
    log_freq = min_log_freq + (max_log_freq - min_log_freq) * random_state.rand()
    freq = np.power(2.0, log_freq)

    signal_y = generate_signal(freq, random_state, noise_ratio)

    return signal_y, freq

# @tools.do_profile(follow=[generate_signal])
def create_train_sample(random_state=None, noise_ratio=None):
    if random_state is None:
        random_state = np.random.RandomState()

    freq_idx = np.random.randint(2, N_CLASSES - 2)
    freq = FUNDAMENTAL_FREQS[freq_idx]
    if noise_ratio is not None:
        freq = freq \
               - freq * noise_ratio / 2.0 \
               + random_state.rand() * freq * noise_ratio

    signal_y = generate_signal(freq, random_state, noise_ratio)

    return signal_y, freq, freq_idx

if __name__ == '__main__':
    output_directory_path = '/home/dankal/bazy/audio/fake/f0analysis_ver2_noise0_0005'

    for i_file in range(100):
        random_state = np.random.RandomState(SEED)
        train_in = []
        train_out = []
        true_freq = []
        for _ in range(TRAIN_SIZE):
            signal_y, freq, net_result = create_train_sample(random_state, NOISE_RATIO)
            train_in.append(signal_y.astype(np.float32))
            train_out.append(net_result)
            true_freq.append(freq)

        file_path = os.path.join(output_directory_path, 'file{0:03d}'.format(i_file))
        tools.save_pickled_data(
            {
                'train_in': train_in,
                'train_out': train_out,
                'true_freq': true_freq,
            },
            file_path,
            make_sure_dir_exist=True
        )


import numpy as np
from matplotlib import pyplot as plt
from matplotlib.font_manager import FontProperties
import math

#计算代价函数
def compute_Cost(X,y,theta):
    m = len(y)
    #代价J
    delta = X*theta-y
    J = np.transpose(delta)*delta/(2*m)
    return J

def gradient(X,y,theta,alpha,num_iters):
    m = len(y)
    n = len(theta)
    #以矩阵形式暂存每次迭代中的theta
    temp = np.matrix(np.zeros((n,num_iters)))
    #每次迭代中的代价值
    J_temp = np.zeros((n,num_iters))
    for i in range(num_iters):
        h = np.dot(X,theta)
        gradient = np.dot((np.transpose(X),h-y))/m
        temp[:,i]=theta-alpha*gradient
        theta = temp[:,i]
        J_temp[i] = compute_Cost(X,y,theta)
    return theta,J_temp

def feature_Normaliza(X):
    X_norm = np.array(X)
    u = np.zeros((1,X.shape[1]))
    sigma = np.zeros((1,X.shape[1]))
    u = np.mean(X_norm,0)
    sigma = np.std(X_norm,0)
    for i in range(X.shape[1]):
        X_norm[:,i] = (X_norm[:,i]-u[i])/sigma[i]
    return X_norm,u,sigma


class Solution:

    def maxProfit(self, prices: list):
        dp_10 = 0  # 完成1笔交易，手上没有持有
        dp_11 = float('-inf')  # 完成1笔交易，手上持有
        dp_20 = 0  # 完成2笔交易，手上没有持有
        dp_21 = float('-inf')  # 完成2笔交易，手上持有

        for price in prices:
            dp_21 = max(dp_21, dp_10 - price)
            dp_20 = max(dp_20, dp_21 + price)
            dp_10 = max(dp_10, dp_11 + price)
            dp_11 = max(dp_11, - price)

        return dp_20

动态规划/123. 买卖股票的最佳时机 III.py


# Copyright (c) 2021 Red Hat
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
from __future__ import absolute_import

import time

from oslo_log import log
import pytest
import testtools

import tobiko
from tobiko import config
from tobiko.openstack import keystone
from tobiko.openstack import neutron
from tobiko.openstack import stacks
from tobiko.shell import ip
from tobiko.shell import iperf3
from tobiko.shell import ping
from tobiko.shell import sh
from tobiko.shell import tcpdump

CONF = config.CONF
LOG = log.getLogger(__name__)

@keystone.skip_unless_has_keystone_credentials()
@neutron.skip_if_is_old_ovn()
class QoSNetworkTest(testtools.TestCase):
    """Tests QoS basic functionality"""

    #: Resources stacks with QoS Policy and QoS Rules and Advanced server
    network = tobiko.required_fixture(stacks.QosNetworkStackFixture)
    policy = tobiko.required_fixture(stacks.QosPolicyStackFixture)
    server = tobiko.required_fixture(stacks.QosServerStackFixture)

    @pytest.mark.flaky(reruns=3, reruns_delay=120)
    def test_ping_dscp(self):
        capture_file = sh.execute('mktemp', sudo=True).stdout.strip()
        interface = ip.get_network_main_route_device(
            self.server.floating_ip_address)

        # IPv4 tcpdump DSCP filters explanation:
        # ip[1] refers to the byte 1 (the TOS byte) of the IP header
        # 0xfc = 11111100 is the mask to get only DSCP value from the ToS
        # As DSCP mark is most significant 6 bits we do right shift (>>)
        # twice in order to divide by 4 and compare with the decimal value
        # See details at http://darenmatthews.com/blog/?p=1199
        dscp_mark = CONF.tobiko.neutron.dscp_mark
        capture_filter = (f"'(ip src {self.server.floating_ip_address} and "
                          f"(ip[1] & 0xfc) >> 2 == {dscp_mark})'")

        # start a capture
        process = tcpdump.start_capture(
            capture_file=capture_file,
            interface=interface,
            capture_filter=capture_filter,
            capture_timeout=60)
        time.sleep(1)
        # send a ping to the server
        ping.assert_reachable_hosts([self.server.floating_ip_address],)
        # stop tcpdump and get the pcap capture
        pcap = tcpdump.get_pcap(process, capture_file=capture_file)
        # check the capture is not empty
        tcpdump.assert_pcap_is_not_empty(pcap=pcap)

    def test_network_qos_policy_id(self):
        """Verify network policy ID"""
        self.assertEqual(self.policy.qos_policy_id,
                         self.network.qos_policy_id)

    def test_server_qos_policy_id(self):
        """Verify server policy ID"""
        self.assertIsNone(self.server.port_details['qos_policy_id'])

    @pytest.mark.flaky(reruns=3, reruns_delay=120)
    def test_qos_bw_limit(self):
        """Verify BW limit using the iperf3 tool"""
        self.server.wait_for_iperf3_server()
        # localhost will act as client
        bandwidth_limit = self.policy.bwlimit_kbps * 1000.
        for attempt in tobiko.retry(timeout=100., interval=5.):
            try:
                iperf3.assert_has_bandwith_limits(
                    address=self.server.ip_address,
                    min_bandwith=bandwidth_limit * 0.9,
                    max_bandwith=bandwidth_limit * 1.1,
                    port=self.server.iperf3_port,
                    download=True)
                break
            except sh.ShellCommandFailed as err:
                if ('unable to connect to server: Connection refused'
                        in str(err)):
                    attempt.check_limits()
                    LOG.debug('iperf command failed because the iperf server '
                              'was not ready yet - retrying...')
                else:
                    raise err


from datetime import datetime
import time
import random
import webbrowser

links = []
with open('links.txt', 'r') as file:
    for link in file:
        print(link)
        links.append(link.strip())

while True:
    # now = datetime.strftime(datetime.now(), "%H")
    # now = int(now)
    # if now >=6 and now <= 23:
    k = random.randrange(len(links))
    url = links[k].split(' - ')[0]
    print(url)
    webbrowser.open(url)
    time.sleep(90*60)


import os
import csv

#set path to csv file
pyBank_csv = os.path.join('..', 'Resources', 'budget_data.csv')

#read the csv file
with open(pyBank_csv, 'r') as PyBankData:
    csvreader = csv.reader(PyBankData, delimiter=",")

    #skip the header
    next(csvreader, None)

    #set variables to 0 for baseline
    totalProfit = 0
    totalMonths = 0
    greatIncrease = 0
    greatDecrease = 0
    averageChange = 0
    totalChange = 0
    maximumChange = 0
    minimumChange = 0

    #set previousRow to zero as baseline
    ##must pop 1st value in monthlyChanges (1st month - baseline)
    previousRow = 0

    #set empty list for monthly change values
    monthlyChanges = []

    #loop through rows
    for row in csvreader:

        #running tally of Months
        totalMonths += 1

        #add to totalProfit
        totalProfit += int(row[1])

        #set current row as thisRow
        thisRow = int(row[1])

        #calculate difference month-to-month (row-to-row)
        monthlyChange = thisRow - previousRow

        #adds monthly difference to monthlyChanges list
        monthlyChanges.append(monthlyChange)

        #set current row as previousRow for next iteration
        previousRow = int(row[1])
       # print(f"Monthly Change: ${monthlyChange}")

#print(monthlyChanges)

maximumChange = max(monthlyChanges)
minimumChange = min(monthlyChanges)

for i in range(0, len(monthlyChanges)):
    totalChange = totalChange + monthlyChanges[i]

             #check for greatest increase or decrease
    #if i['monthlyChange'] = maximumChange:
     #   greatIncrease = i['monthlyChange']
    #if i['monthyChange'] < minimumChange:
      #  greatDecrease = i['monthlyChange']

averageChange = totalChange/(totalMonths - 1)

#truncate all but 2 decimals in average monthly change
averageChange = float('%.2f'%(averageChange))

print("Financial Analysis")
print("-------------------")
print(f"Total Months: {totalMonths}")
print(f"Total Profit: $ {totalProfit}")
print (f"Total Change: $ {totalChange}")
print(f"Average Change: $ {averageChange}")
print(f"Greatest Increase in Profits: ${greatIncrease}")
print(f"Greatest Decrease in Profits: -${abs(greatDecrease)}")


# arrays are series of spaces

# Static Arrays
arr = [1,2,3,4] # this will be 4 spaces long, next to each other
# the arr itself points to the first value in the arr
# if you want to access the other values, add 1,2,3 etc to get the location you want

'''
- arrays access items at constant time O(1)
- arr.append() is O(n)
- changing an item in the arr is O(1)
- deleting an item is O(n) since you have to shuffle items to the left
- arr.insert() is O(n) since things have to shuffle to the right
'''

num = 1


class Yahtzee:

    @staticmethod
    def chance(d1, d2, d3, d4, d5):
        return d1 + d2 + d3 + d4 + d5

    @staticmethod
    def yahtzee(dice):
        if dice.count(dice[0]) == 5:
            return 50
        else:
            return 0

    @staticmethod
    def ones(d1,  d2,  d3,  d4,  d5):
        lista = [d1, d2, d3, d4, d5]
        return lista.count(1)

    @staticmethod
    def twos(d1,  d2,  d3,  d4,  d5):
        lista = [d1, d2, d3, d4, d5]
        return 2*lista.count(2)

    @staticmethod
    def threes(d1,  d2,  d3,  d4,  d5):
        lista = [d1, d2, d3, d4, d5]
        return 3*lista.count(3)

    def __init__(self, d1, d2, d3, d4, d5):
        self.dice = [d1, d2, d3, d4, d5]

    def fours(self):
        return 4*self.dice.count(4)

    def fives(self):
        return 5*self.dice.count(5)

    def sixes(self):
        return 6*self.dice.count(6)

    @staticmethod
    def score_pair(d1,  d2,  d3,  d4,  d5):
        counts = Yahtzee.contador(d1, d2, d3, d4, d5)
        lista = []
        for i in range(5, -1, -1):
            if (counts[i] >= 2):
                return (i+1)*2
        else:
            return 0

    @staticmethod
    def two_pair(d1,  d2,  d3,  d4,  d5):
        counts = Yahtzee.contador(d1, d2, d3, d4, d5)
        n = score = 0
        for i in range(6):
            if (counts[i] >= 2):
                n = n+1
                score += (i+1)
            if (n == 2):
                return score * 2
        else:
            return 0

    @staticmethod
    def four_of_a_kind(d1,  d2,  d3,  d4,  d5):
        lista = [d1, d2, d3, d4, d5]
        if lista.count(lista[0]) >= 4:
            return lista[0]*4
        elif lista.count(lista[1]) >= 4:
            return lista[1]*4
        else:
            return 0

    @staticmethod
    def three_of_a_kind(d1,  d2,  d3,  d4,  d5):
        lista = [d1, d2, d3, d4, d5]
        if lista.count(lista[0]) >= 3:
            return lista[0]*3
        elif lista.count(lista[1]) >= 3:
            return lista[1]*3
        elif lista.count(lista[2]) >= 3:
            return lista[2]*3
        else:
            return 0

    @staticmethod
    def smallStraight(d1,  d2,  d3,  d4,  d5):
        if sorted((d1, d2, d3, d4, d5)) == [1, 2, 3, 4, 5]:
            return 15
        else:
            return 0

    @staticmethod
    def largeStraight(d1,  d2,  d3,  d4,  d5):
        if sorted((d1, d2, d3, d4, d5)) == [2, 3, 4, 5, 6]:
            return 20
        else:
            return 0

    @staticmethod
    def fullHouse(d1,  d2,  d3,  d4,  d5):
        counts = Yahtzee.contador(d1, d2, d3, d4, d5)
        if 2 in counts and 3 in counts:
            return (counts.index(2)+1)*2    +      (counts.index(3)+1)*3
        else:
            return 0

    @staticmethod
    def contador(d1, d2, d3, d4, d5):
        counts = [0]*6
        counts[d1-1] += 1
        counts[d2-1] += 1
        counts[d3-1] += 1
        counts[d4-1] += 1
        counts[d5-1] += 1
        return counts


def Send_Thanks(dnrslst):
    response = "Not a donor or the word list"

    while response not in dnrslst:
        print("----------------------------------------")
        response = input("Who would you like to thank ('first last', 'list', or 'exit'? ")
        response = response.title()

        if response == "Exit":
            break
        elif response == "List":
            print("----------------------------------------")
            print("Below is the list of Donors:")
            print(List_Donors(dnrslst))
        elif response != "List":
            print("----------------------------------------")
            amount = input("How much was dontated by {}? ".format(response))
            dnrslst = Rec_Donation(dnrslst, response, amount)
            print("----------------------------------------")
            Thanks_Email(response, dnrslst)

def List_Donors(dnrslst):
    name_lst = []
    for i in dnrslst:
        if i[0] not in name_lst:
            name_lst.append(i[0])
    return name_lst

def Rec_Donation(dnrslst, donor, money):
    donor_tpl = (donor, money)
    dnrslst.append(donor_tpl)
    return dnrslst

def Thanks_Email(donor, dnrslst):
    money = Sum_Gift(donor, dnrslst)
    print("Dear {},".format(donor))
    print("Thank you for your generous gift of {}.".format(money))

def Sum_Gift(donor, dnrslst):
    total = 0
    for i in dnrslst:
        if i[0] == donor:
            total = total + int(i[1])
    return total

def donation_count(donor, dnrslst):
    count = 0
    for i in dnrslst:
        if i[0] == donor:
            count = count + 1
    return count

def Create_Report(dnrslst):
    dnrNames = List_Donors(dnrslst)
    print(f'{"Donor Name":<20}{"|":<1}{"Total Given":^15}{"|":<1}{"Num Gifts":^15}{"|":<1}{"Average Gift":>15}')
    print("-" * 68)
    iCount = 1
    for i in dnrNames:
        iTotalGift = Sum_Gift(i, dnrslst)
        iCount = donation_count(i, dnrslst)
        iAveGift = iTotalGift / iCount
        s = f'{i:<20}{" ":<1}{"$":<1}{iTotalGift:>14}{" ":<1}{iCount:>15}{" ":<1}{"$":<1}{iAveGift:>14}'
        print(s)

if __name__ == '__main__':
    donors = [("Anita Bath", 200),("Isabelle Ringing", 101), ("John Smith", 100), ("Seymour Butz", 95), ("Anita Bath", 150)]
    response = 123456

    while True:
        print("Would you like to;")
        print("1: Send a 'Thank You'")
        print("2: Create a 'Report'")
        print("3: 'Quit'")
        response = input("Please select option: ")

        if response.title() == "Thank You":
            Send_Thanks(donors)
        elif response.title() == "Report":
            Create_Report(donors)
        elif response.title() == "Quit":
            break
        print()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
from bacpypes.primitivedata import Real
from bacpypes.object import Property
from bacpypes.errors import ExecutionError

from eyed.single import SingleBACnetdService
from datastore import DatastoreType

#
# Database 接続用
#
from eyed.model import BACnetSimulationLog, BACnetMeasuredValue, BACnetTask
from eyed.db import SessionFactory

#
# Eyed Present Value
#
class EyedPresentValue(Property):
	#
	# コンストラクタ
	#
	def __init__(self, object_id, instance_id, default_value = 0, type = DatastoreType.STATIC):
		#
		# 各識別子の定義
		#
		self.key		= None
		self.type		= type
		self.object_id		= object_id
		self.instance_id	= instance_id
		self.property_id	= 85
		self.identifier		= 'presentValue'

		#
		# スーパクラスのコンストラクタ呼び出し
		#
		Property.__init__(
			self,
			self.identifier,
			Real,
			default=0.0,
			optional=True,
			mutable=False
		)

		#
		# 初期値のセットアップ
		#
		self.setType(type, default_value)

	#
	# 読み込み
	#
	def ReadProperty(self, obj, arrayIndex=None):
		#
		# Access an array
		#
		if arrayIndex is not None:
			raise ExecutionError(errorClass='property', errorCode='propertyIsNotAnArray')

		#
		# キャッシュに値があれば、キャシュの値を返す
		#
		datastore = SingleBACnetdService().getDatastore()
		value = datastore.getValue(self.key)

		#
		# DB への 接続
		#
		with SessionFactory() as session:
			#
			# DBへの登録
			#
			session.add(BACnetSimulationLog(self.object_id, self.instance_id, self.property_id, value))
			session.commit()

		#
		# 値の返却
		#
		if not value == None:
			return value
		raise ExecutionError(errorClass='property', errorCode='abortProprietary')

	#
	# 書き込み
	#
	def WriteProperty(self, obj, value, arrayIndex=None, priority=None, direct=False):
		raise ExecutionError(errorClass='property', errorCode='writeAccessDenied')

	#
	# プロパティ種別の変更
	#
	def setType(self, type, value):
		#
		# プロパティ種別の設定
		#
		self.type = type
		datastore = SingleBACnetdService().getDatastore()

		#
		# プロパティ種別が「STATIC」の場合
		#
		if type == DatastoreType.STATIC:
			#
			# 鍵の取得
			#
			self.key = datastore.getBACnetKey(
				DatastoreType.STATIC,
				self.object_id,
				self.instance_id,
				self.property_id,
			)

			#
			# 初期値の設定
			#
			datastore.setBACnetValue(
				DatastoreType.STATIC,
				self.object_id,
				self.instance_id,
				self.property_id,
				value
			)
			return True
		#
		# プロパティ種別が「MEASUREMENT」の場合
		#
		else:
			#
			# DB への 接続
			#
			with SessionFactory() as session:
				#
				# タスクの確認
				#
				task = session.query(BACnetTask).filter_by(id = value).first()

				#
				# 鍵の取得
				#
				self.key = datastore.getBACnetKey(
					DatastoreType.MEASUREMENT,
					task.object_id,
					task.instance_id,
					task.property_id,
				)
			return True
		return False


# coding: utf-8
from pyquery import PyQuery as q
from collections import OrderedDict
from tools import *
from logging import warn

PROPERTIES_REFERENCE = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-product-property-reference.html'

def property_name_from_href(href):
    href = str(href)
    href, _ = href.rsplit(".", 1)
    _, href = href.rsplit("/", 1)
    return href

def property_ref_from_href(href):
    return {
        '$ref':
        '#/definitions/property_types/%s' % property_name_from_href(href)
    }

def get_type(dd_):
    dd = dd_('p').filter(lambda x: q(this).text().startswith('Type'))
    t = dd.text().lower()
    if 'type : string' in t:
        return {'type': 'string'}
    if 'list of strings' in t:
        return {'type': 'array', 'items': {'type': 'string'}}
    if 'type : integer' in t:
        return {'type': 'integer'}
    if 'type : boolean' in t:
        return {'type': 'boolean'}
    if dd('a'):
        return property_ref_from_href(dd('a').attr('href'))
    if dd_('.type') and len(dd_('.type')):
        if (dd_('.type').text() == 'AWS::EC2::SecurityGroup' and
                'list of' in t):
            return {'type': 'array', 'items': {'type': 'string'}}

    warn('Could not parse resource property type: "%s"', dd_.html())
    return {}

all_properties = all_resource_properties_hrefs()

def set_resource_properties(res_type):
    all = all_resource_hrefs()
    h = get_pq(all[res_type])
    schema = load()
    dl = h('#divContent .variablelist dl').eq(0)
    resources = resources_dict(schema)
    pairs = zip(dl('dt'), dl('dd'))
    pairs = [(q(dt), q(dd)) for dt, dd in pairs]
    shortcut = resources[res_type]['properties']
    shortcut['Properties'] = OrderedDict()
    shortcut['Properties']['properties'] = OrderedDict(
        (dt.text(), get_type(dd))
        for dt, dd in pairs
    )
    required = [
        k.text()
        for k, v
        in pairs
        if v('p').filter(lambda i: 'Required : Yes' in q(this).text())
    ]
    if required:
        shortcut['Properties']['required'] = required
        resources[res_type]['required'] = ['Properties']
    return schema

def all_properties():
    h = get_pq(PROPERTIES_REFERENCE)
    res = OrderedDict(
        (
            property_name_from_href(q(a).attr("href")),
            {
                "title": " ".join(a.text.split()),
                "description": q(a).attr("href")
            }
        ) for a in h('#divContent li a'))
    return res


"""Flic client library for python

Requires python 3.3 or higher.

For detailed documentation, see the protocol documentation.

Notes on the data type used in this python implementation compared to the protocol documentation:
All kind of integers are represented as python integers.
Booleans use the Boolean type.
Enums use the defined python enums below.
Bd addr are represented as standard python strings, e.g. "aa:bb:cc:dd:ee:ff".
"""
import asyncio
from enum import Enum
from collections import namedtuple
import struct
import itertools
import threading

class CreateConnectionChannelError(Enum):
    NoError = 0
    MaxPendingConnectionsReached = 1

class ConnectionStatus(Enum):
    Disconnected = 0
    Connected = 1
    Ready = 2

class DisconnectReason(Enum):
    Unspecified = 0
    ConnectionEstablishmentFailed = 1
    TimedOut = 2
    BondingKeysMismatch = 3

class RemovedReason(Enum):
    RemovedByThisClient = 0
    ForceDisconnectedByThisClient = 1
    ForceDisconnectedByOtherClient = 2

    ButtonIsPrivate = 3
    VerifyTimeout = 4
    InternetBackendError = 5
    InvalidData = 6

    CouldntLoadDevice = 7

class ClickType(Enum):
    ButtonDown = 0
    ButtonUp = 1
    ButtonClick = 2
    ButtonSingleClick = 3
    ButtonDoubleClick = 4
    ButtonHold = 5

class BdAddrType(Enum):
    PublicBdAddrType = 0
    RandomBdAddrType = 1

class LatencyMode(Enum):
    NormalLatency = 0
    LowLatency = 1
    HighLatency = 2

class BluetoothControllerState(Enum):
    Detached = 0
    Resetting = 1
    Attached = 2

class ScanWizardResult(Enum):
    WizardSuccess = 0
    WizardCancelledByUser = 1
    WizardFailedTimeout = 2
    WizardButtonIsPrivate = 3
    WizardBluetoothUnavailable = 4
    WizardInternetBackendError = 5
    WizardInvalidData = 6

class ButtonScanner:
    """ButtonScanner class.

    Usage:
    scanner = ButtonScanner()
    scanner.on_advertisement_packet = lambda scanner, bd_addr, name, rssi, is_private, already_verified: ...
    client.add_scanner(scanner)
    """

    _cnt = itertools.count()

    def __init__(self):
        self._scan_id = next(ButtonScanner._cnt)
        self.on_advertisement_packet = lambda scanner, bd_addr, name, rssi, is_private, already_verified: None

class ScanWizard:
    """ScanWizard class

    Usage:
    wizard = ScanWizard()
    wizard.on_found_private_button = lambda scan_wizard: ...
    wizard.on_found_public_button = lambda scan_wizard, bd_addr, name: ...
    wizard.on_button_connected = lambda scan_wizard, bd_addr, name: ...
    wizard.on_completed = lambda scan_wizard, result, bd_addr, name: ...
    client.add_scan_wizard(wizard)
    """

    _cnt = itertools.count()

    def __init__(self):
        self._scan_wizard_id = next(ScanWizard._cnt)
        self._bd_addr = None
        self._name = None
        self.on_found_private_button = lambda scan_wizard: None
        self.on_found_public_button = lambda scan_wizard, bd_addr, name: None
        self.on_button_connected = lambda scan_wizard, bd_addr, name: None
        self.on_completed = lambda scan_wizard, result, bd_addr, name: None

class ButtonConnectionChannel:
    """ButtonConnectionChannel class.

    This class represents a connection channel to a Flic button.
    Add this button connection channel to a FlicClient by executing client.add_connection_channel(connection_channel).
    You may only have this connection channel added to one FlicClient at a time.

    Before you add the connection channel to the client, you should set up your callback functions by assigning
    the corresponding properties to this object with a function. Each callback function has a channel parameter as the first one,
    referencing this object.

    Available properties and the function parameters are:
    on_create_connection_channel_response: channel, error, connection_status
    on_removed: channel, removed_reason
    on_connection_status_changed: channel, connection_status, disconnect_reason
    on_button_up_or_down / on_button_click_or_hold / on_button_single_or_double_click / on_button_single_or_double_click_or_hold: channel, click_type, was_queued, time_diff
    """

    _cnt = itertools.count()

    def __init__(self, bd_addr, latency_mode=LatencyMode.NormalLatency, auto_disconnect_time=511):
        self._conn_id = next(ButtonConnectionChannel._cnt)
        self._bd_addr = bd_addr
        self._latency_mode = latency_mode
        self._auto_disconnect_time = auto_disconnect_time
        self._client = None

        self.on_create_connection_channel_response = lambda channel, error, connection_status: None
        self.on_removed = lambda channel, removed_reason: None
        self.on_connection_status_changed = lambda channel, connection_status, disconnect_reason: None
        self.on_button_up_or_down = lambda channel, click_type, was_queued, time_diff: None
        self.on_button_click_or_hold = lambda channel, click_type, was_queued, time_diff: None
        self.on_button_single_or_double_click = lambda channel, click_type, was_queued, time_diff: None
        self.on_button_single_or_double_click_or_hold = lambda channel, click_type, was_queued, time_diff: None

    @property
    def bd_addr(self):
        return self._bd_addr

    @property
    def latency_mode(self):
        return self._latency_mode

    @latency_mode.setter
    def latency_mode(self, latency_mode):
        if self._client is None:
            self._latency_mode = latency_mode
            return

        self._latency_mode = latency_mode
        if not self._client._closed:
            self._client._send_command("CmdChangeModeParameters",
                                       {"conn_id": self._conn_id, "latency_mode": self._latency_mode,
                                        "auto_disconnect_time": self._auto_disconnect_time})

    @property
    def auto_disconnect_time(self):
        return self._auto_disconnect_time

    @auto_disconnect_time.setter
    def auto_disconnect_time(self, auto_disconnect_time):
        if self._client is None:
            self._auto_disconnect_time = auto_disconnect_time
            return

        self._auto_disconnect_time = auto_disconnect_time
        if not self._client._closed:
            self._client._send_command("CmdChangeModeParameters",
                                       {"conn_id": self._conn_id, "latency_mode": self._latency_mode,
                                        "auto_disconnect_time": self._auto_disconnect_time})

class FlicClient(asyncio.Protocol):
    """FlicClient class.

    When this class is constructed, a socket connection is established.
    You may then send commands to the server and set timers.
    Once you are ready with the initialization you must call the handle_events() method which is a main loop that never exits, unless the socket is closed.
    For a more detailed description of all commands, events and enums, check the protocol specification.

    All commands are wrapped in more high level functions and events are reported using callback functions.

    All methods called on this class will take effect only if you eventually call the handle_events() method.

    The ButtonScanner is used to set up a handler for advertisement packets.
    The ButtonConnectionChannel is used to interact with connections to flic buttons and receive their events.

    Other events are handled by the following callback functions that can be assigned to this object (and a list of the callback function parameters):
    on_new_verified_button: bd_addr
    on_no_space_for_new_connection: max_concurrently_connected_buttons
    on_got_space_for_new_connection: max_concurrently_connected_buttons
    on_bluetooth_controller_state_change: state
    """

    _EVENTS = [
        ("EvtAdvertisementPacket", "<I6s17pb??", "scan_id bd_addr name rssi is_private already_verified"),
        ("EvtCreateConnectionChannelResponse", "<IBB", "conn_id error connection_status"),
        ("EvtConnectionStatusChanged", "<IBB", "conn_id connection_status disconnect_reason"),
        ("EvtConnectionChannelRemoved", "<IB", "conn_id removed_reason"),
        ("EvtButtonUpOrDown", "<IBBI", "conn_id click_type was_queued time_diff"),
        ("EvtButtonClickOrHold", "<IBBI", "conn_id click_type was_queued time_diff"),
        ("EvtButtonSingleOrDoubleClick", "<IBBI", "conn_id click_type was_queued time_diff"),
        ("EvtButtonSingleOrDoubleClickOrHold", "<IBBI", "conn_id click_type was_queued time_diff"),
        ("EvtNewVerifiedButton", "<6s", "bd_addr"),
        ("EvtGetInfoResponse", "<B6sBBhBBH",
         "bluetooth_controller_state my_bd_addr my_bd_addr_type max_pending_connections max_concurrently_connected_buttons current_pending_connections currently_no_space_for_new_connection nb_verified_buttons"),
        ("EvtNoSpaceForNewConnection", "<B", "max_concurrently_connected_buttons"),
        ("EvtGotSpaceForNewConnection", "<B", "max_concurrently_connected_buttons"),
        ("EvtBluetoothControllerStateChange", "<B", "state"),
        ("EvtPingResponse", "<I", "ping_id"),
        ("EvtGetButtonUUIDResponse", "<6s16s", "bd_addr uuid"),
        ("EvtScanWizardFoundPrivateButton", "<I", "scan_wizard_id"),
        ("EvtScanWizardFoundPublicButton", "<I6s17p", "scan_wizard_id bd_addr name"),
        ("EvtScanWizardButtonConnected", "<I", "scan_wizard_id"),
        ("EvtScanWizardCompleted", "<IB", "scan_wizard_id result")
    ]
    _EVENT_STRUCTS = list(map(lambda x: None if x is None else struct.Struct(x[1]), _EVENTS))
    _EVENT_NAMED_TUPLES = list(map(lambda x: None if x is None else namedtuple(x[0], x[2]), _EVENTS))

    _COMMANDS = [
        ("CmdGetInfo", "", ""),
        ("CmdCreateScanner", "<I", "scan_id"),
        ("CmdRemoveScanner", "<I", "scan_id"),
        ("CmdCreateConnectionChannel", "<I6sBh", "conn_id bd_addr latency_mode auto_disconnect_time"),
        ("CmdRemoveConnectionChannel", "<I", "conn_id"),
        ("CmdForceDisconnect", "<6s", "bd_addr"),
        ("CmdChangeModeParameters", "<IBh", "conn_id latency_mode auto_disconnect_time"),
        ("CmdPing", "<I", "ping_id"),
        ("CmdGetButtonUUID", "<6s", "bd_addr"),
        ("CmdCreateScanWizard", "<I", "scan_wizard_id"),
        ("CmdCancelScanWizard", "<I", "scan_wizard_id")
    ]

    _COMMAND_STRUCTS = list(map(lambda x: struct.Struct(x[1]), _COMMANDS))
    _COMMAND_NAMED_TUPLES = list(map(lambda x: namedtuple(x[0], x[2]), _COMMANDS))
    _COMMAND_NAME_TO_OPCODE = dict((x[0], i) for i, x in enumerate(_COMMANDS))

    @staticmethod
    def _bdaddr_bytes_to_string(bdaddr_bytes):
        return ":".join(map(lambda x: "%02x" % x, reversed(bdaddr_bytes)))

    @staticmethod
    def _bdaddr_string_to_bytes(bdaddr_string):
        return bytearray.fromhex("".join(reversed(bdaddr_string.split(":"))))

    def __init__(self, loop, parent=None):
        self.loop = loop
        self.buffer = b""
        self.transport = None
        self.parent = parent
        self._scanners = {}
        self._scan_wizards = {}
        self._connection_channels = {}
        self._closed = False

        self.on_new_verified_button = lambda bd_addr: None
        self.on_no_space_for_new_connection = lambda max_concurrently_connected_buttons: None
        self.on_got_space_for_new_connection = lambda max_concurrently_connected_buttons: None
        self.on_bluetooth_controller_state_change = lambda state: None
        self.on_get_info = lambda items: None
        self.on_get_button_uuid = lambda addr, uuid: None

    def connection_made(self, transport):
        self.transport = transport
        if self.parent:
            self.parent.register_protocol(self)

    def close(self):
        """Closes the client. The handle_events() method will return."""
        if self._closed:
            return

        self._closed = True

    def add_scanner(self, scanner):
        """Add a ButtonScanner object.

        The scan will start directly once the scanner is added.
        """
        if scanner._scan_id in self._scanners:
            return

        self._scanners[scanner._scan_id] = scanner
        self._send_command("CmdCreateScanner", {"scan_id": scanner._scan_id})

    def remove_scanner(self, scanner):
        """Remove a ButtonScanner object.

        You will no longer receive advertisement packets.
        """
        if scanner._scan_id not in self._scanners:
            return

        del self._scanners[scanner._scan_id]
        self._send_command("CmdRemoveScanner", {"scan_id": scanner._scan_id})

    def add_scan_wizard(self, scan_wizard):
        """Add a ScanWizard object.

        The scan wizard will start directly once the scan wizard is added.
        """
        if scan_wizard._scan_wizard_id in self._scan_wizards:
            return

        self._scan_wizards[scan_wizard._scan_wizard_id] = scan_wizard
        self._send_command("CmdCreateScanWizard", {"scan_wizard_id": scan_wizard._scan_wizard_id})

    def cancel_scan_wizard(self, scan_wizard):
        """Cancel a ScanWizard.

        Note: The effect of this command will take place at the time the on_completed event arrives on the scan wizard object.
        If cancelled due to this command, "result" in the on_completed event will be "WizardCancelledByUser".
        """
        if scan_wizard._scan_wizard_id not in self._scan_wizards:
            return

        self._send_command("CmdCancelScanWizard", {"scan_wizard_id": scan_wizard._scan_wizard_id})

    def add_connection_channel(self, channel):
        """Adds a connection channel to a specific Flic button.

        This will start listening for a specific Flic button's connection and button events.
        Make sure the Flic is either in public mode (by holding it down for 7 seconds) or already verified before calling this method.

        The on_create_connection_channel_response callback property will be called on the
        connection channel after this command has been received by the server.

        You may have as many connection channels as you wish for a specific Flic Button.
        """
        if channel._conn_id in self._connection_channels:
            return

        channel._client = self

        self._connection_channels[channel._conn_id] = channel
        self._send_command("CmdCreateConnectionChannel", {"conn_id": channel._conn_id, "bd_addr": channel.bd_addr,
                                                          "latency_mode": channel._latency_mode,
                                                          "auto_disconnect_time": channel._auto_disconnect_time})

    def remove_connection_channel(self, channel):
        """Remove a connection channel.

        This will stop listening for new events for a specific connection channel that has previously been added.
        Note: The effect of this command will take place at the time the on_removed event arrives on the connection channel object.
        """
        if channel._conn_id not in self._connection_channels:
            return

        self._send_command("CmdRemoveConnectionChannel", {"conn_id": channel._conn_id})

    def force_disconnect(self, bd_addr):
        """Force disconnection or cancel pending connection of a specific Flic button.

        This removes all connection channels for all clients connected to the server for this specific Flic button.
        """
        self._send_command("CmdForceDisconnect", {"bd_addr": bd_addr})

    def get_info(self):
        """Get info about the current state of the server.

        The server will send back its information directly and the callback will be called once the response arrives.
        The callback takes only one parameter: info. This info parameter is a dictionary with the following objects:
        bluetooth_controller_state, my_bd_addr, my_bd_addr_type, max_pending_connections, max_concurrently_connected_buttons,
        current_pending_connections, currently_no_space_for_new_connection, bd_addr_of_verified_buttons (a list of bd addresses).
        """
        self._send_command("CmdGetInfo", {})

    def get_button_uuid(self, bd_addr):
        """Get button uuid for a verified button.

        The server will send back its information directly and the callback will be called once the response arrives.
        Responses will arrive in the same order as requested.

        The callback takes two parameters: bd_addr, uuid (hex string of 32 characters).

        Note: if the button isn't verified, the uuid sent to the callback will rather be None.
        """
        self._send_command("CmdGetButtonUUID", {"bd_addr": bd_addr})

    def run_on_handle_events_thread(self, callback):
        """Run a function on the thread that handles the events."""
        if threading.get_ident() == self._handle_event_thread_ident:
            callback()
        else:
            self.set_timer(0, callback)

    def _send_command(self, name, items):

        for key, value in items.items():
            if isinstance(value, Enum):
                items[key] = value.value

        if "bd_addr" in items:
            items["bd_addr"] = FlicClient._bdaddr_string_to_bytes()

        opcode = FlicClient._COMMAND_NAME_TO_OPCODE[name]
        data_bytes = FlicClient._COMMAND_STRUCTS[opcode].pack(*FlicClient._COMMAND_NAMED_TUPLES[opcode](**items))
        bytes = bytearray(3)
        bytes[0] = (len(data_bytes) + 1) & 0xff
        bytes[1] = (len(data_bytes) + 1) >> 8
        bytes[2] = opcode
        bytes += data_bytes
        self.transport.write(bytes)

    def _dispatch_event(self, data):
        if len(data) == 0:
            return
        opcode = data[0]

        if opcode >= len(FlicClient._EVENTS) or FlicClient._EVENTS[opcode] is None:
            return

        event_name = FlicClient._EVENTS[opcode][0]
        data_tuple = FlicClient._EVENT_STRUCTS[opcode].unpack(data[1: 1 + FlicClient._EVENT_STRUCTS[opcode].size])
        items = FlicClient._EVENT_NAMED_TUPLES[opcode]._make(data_tuple)._asdict()

        # Process some kind of items whose data type is not supported by struct
        if "bd_addr" in items:
            items["bd_addr"] = FlicClient._bdaddr_bytes_to_string()

        if "name" in items:
            items["name"] = items["name"].decode("utf-8")

        if event_name == "EvtCreateConnectionChannelResponse":
            items["error"] = CreateConnectionChannelError(items["error"])
            items["connection_status"] = ConnectionStatus(items["connection_status"])

        if event_name == "EvtConnectionStatusChanged":
            items["connection_status"] = ConnectionStatus(items["connection_status"])
            items["disconnect_reason"] = DisconnectReason(items["disconnect_reason"])

        if event_name == "EvtConnectionChannelRemoved":
            items["removed_reason"] = RemovedReason(items["removed_reason"])

        if event_name.startswith("EvtButton"):
            items["click_type"] = ClickType(items["click_type"])

        if event_name == "EvtGetInfoResponse":
            items["bluetooth_controller_state"] = BluetoothControllerState(items["bluetooth_controller_state"])
            items["my_bd_addr"] = FlicClient._bdaddr_bytes_to_string()
            items["my_bd_addr_type"] = BdAddrType(items["my_bd_addr_type"])
            items["bd_addr_of_verified_buttons"] = []

            pos = FlicClient._EVENT_STRUCTS[opcode].size
            for i in range(items["nb_verified_buttons"]):
                items["bd_addr_of_verified_buttons"].append(
                    FlicClient._bdaddr_bytes_to_string())
                pos += 6

        if event_name == "EvtBluetoothControllerStateChange":
            items["state"] = BluetoothControllerState(items["state"])

        if event_name == "EvtGetButtonUUIDResponse":
            items["uuid"] = "".join(map(lambda x: "%02x" % x, items["uuid"]))
            if items["uuid"] == "00000000000000000000000000000000":
                items["uuid"] = None

        if event_name == "EvtScanWizardCompleted":
            items["result"] = ScanWizardResult(items["result"])

        # Process event
        if event_name == "EvtAdvertisementPacket":
            scanner = self._scanners.get(items["scan_id"])
            if scanner is not None:
                scanner.on_advertisement_packet(scanner, items["bd_addr"], items["name"], items["rssi"],
                                                items["is_private"], items["already_verified"])

        if event_name == "EvtCreateConnectionChannelResponse":
            channel = self._connection_channels[items["conn_id"]]
            if items["error"] != CreateConnectionChannelError.NoError:
                del self._connection_channels[items["conn_id"]]
            channel.on_create_connection_channel_response(channel, items["error"], items["connection_status"])

        if event_name == "EvtConnectionStatusChanged":
            channel = self._connection_channels[items["conn_id"]]
            channel.on_connection_status_changed(channel, items["connection_status"], items["disconnect_reason"])

        if event_name == "EvtConnectionChannelRemoved":
            channel = self._connection_channels[items["conn_id"]]
            del self._connection_channels[items["conn_id"]]
            channel.on_removed(channel, items["removed_reason"])

        if event_name == "EvtButtonUpOrDown":
            channel = self._connection_channels[items["conn_id"]]
            channel.on_button_up_or_down(channel, items["click_type"], items["was_queued"], items["time_diff"])
        if event_name == "EvtButtonClickOrHold":
            channel = self._connection_channels[items["conn_id"]]
            channel.on_button_click_or_hold(channel, items["click_type"], items["was_queued"], items["time_diff"])
        if event_name == "EvtButtonSingleOrDoubleClick":
            channel = self._connection_channels[items["conn_id"]]
            channel.on_button_single_or_double_click(channel, items["click_type"], items["was_queued"],
                                                     items["time_diff"])
        if event_name == "EvtButtonSingleOrDoubleClickOrHold":
            channel = self._connection_channels[items["conn_id"]]
            channel.on_button_single_or_double_click_or_hold(channel, items["click_type"], items["was_queued"],
                                                             items["time_diff"])

        if event_name == "EvtNewVerifiedButton":
            self.on_new_verified_button(items["bd_addr"])

        if event_name == "EvtGetInfoResponse":
            self.on_get_info(items)

        if event_name == "EvtNoSpaceForNewConnection":
            self.on_no_space_for_new_connection(items["max_concurrently_connected_buttons"])

        if event_name == "EvtGotSpaceForNewConnection":
            self.on_got_space_for_new_connection(items["max_concurrently_connected_buttons"])

        if event_name == "EvtBluetoothControllerStateChange":
            self.on_bluetooth_controller_state_change(items["state"])

        if event_name == "EvtGetButtonUUIDResponse":
            self.on_get_button_uuid(items["bd_addr"], items["uuid"])

        if event_name == "EvtScanWizardFoundPrivateButton":
            scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
            scan_wizard.on_found_private_button(scan_wizard)

        if event_name == "EvtScanWizardFoundPublicButton":
            scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
            scan_wizard._bd_addr = items["bd_addr"]
            scan_wizard._name = items["name"]
            scan_wizard.on_found_public_button(scan_wizard, scan_wizard._bd_addr, scan_wizard._name)

        if event_name == "EvtScanWizardButtonConnected":
            scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
            scan_wizard.on_button_connected(scan_wizard, scan_wizard._bd_addr, scan_wizard._name)

        if event_name == "EvtScanWizardCompleted":
            scan_wizard = self._scan_wizards[items["scan_wizard_id"]]
            del self._scan_wizards[items["scan_wizard_id"]]
            scan_wizard.on_completed(scan_wizard, items["result"], scan_wizard._bd_addr, scan_wizard._name)

    def data_received(self, data):
        cdata = self.buffer + data
        self.buffer = b""
        while len(cdata):
            packet_len = cdata[0] | (cdata[1] << 8)
            packet_len += 2
            if len(cdata) >= packet_len:
                self._dispatch_event(cdata[2:packet_len])
                cdata = cdata[packet_len:]
            else:
                if len(cdata):
                    self.buffer = cdata  # unlikely to happen but.....
                break


import socket
import random
import string

from Crypto.Cipher import AES

# Porneste discutia cu generatorul pentru a lua cheia si vectorul de initializare

def generator_discussion():
    s = socket.socket()
    host = socket.gethostname()
    port = 111

    s.connect((host, port))
    K = s.recv(1024)
    iv = s.recv(1024)
    s.close()

    return K, iv

# Discuta cu nodurile A si B pentru a le oferi cheile criptate pentru
# criptarea/decriptare mesajului (in functie de modul de operare dorit)

def A_B_discussion():
    s = socket.socket()
    host = socket.gethostname()
    port = 112

    s.bind((host, port))

    s.listen()
    if True:
        c, adr = s.accept()
        print("Got conection with node A")
        mesaj = c.recv(1024)
        if mesaj == b"ecb":
            print("\nTrimitem cheia criptata k1 lui A:  ", ciphertext_k1)
            c.send(ciphertext_k1)
        else:
            print("\nTrimitem cheia criptata k2 lui A:  ", ciphertext_k2)
            c.send(ciphertext_k2)
        c.close()

    if True:
        c, adr = s.accept()
        print("Got conection with node B")
        mesaj = c.recv(1024)
        if mesaj == b"ecb":
            print("\nTrimitem cheia criptata k1 lui B:  ", ciphertext_k1)
            c.send(ciphertext_k1)
        else:
            print("\nTrimitem cheia criptata k1 lui B:  ", ciphertext_k1)
            c.send(ciphertext_k2)
        c.close()

    s.close()

# Realizeaza cheile k1(modul ECB) si k2(modul CFB) random

def random_key():
    k1 = str.encode(
        ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=key_length // 8)))
    k2 = str.encode(
        ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=key_length // 8)))
    return k1, k2

# Cripteaza cheile k1 si k2 pentru a le trimite mai departe nodurilor A si B
# (atunci cand sunt cerute)

def encrypt_k1(k):
    key_encrypt = AES.new(K, AES.MODE_ECB)
    ciphertext = key_encrypt.encrypt(k)
    return ciphertext

def encrypt_k2(k):
    iv_encrypt = AES.new(K, AES.MODE_ECB)
    cipher = iv_encrypt.encrypt(iv)
    ciphertext = bytes([_a ^ _b for _a, _b in zip(cipher, k)])
    return ciphertext

key_length = 128

K, iv = generator_discussion()
print("Am preluat valorile generate:  ", K, iv)

k1, k2 = random_key()
print("\nGeneram cheile k1 si k2:  ", k1 , k2)

ciphertext_k1 = encrypt_k1(k1)
ciphertext_k2 = encrypt_k2(k2)

A_B_discussion()


#!/usr/bin/python3
import random

def main():
    # Basic game board initialization.
    game_board = [
        ['', '', ''],
        ['', '', ''],
        ['', '', '']
    ]
    # Call to display_board. Comment this out once
    # you have it working.
    #display_board(game_board)

    # Initialize the game state by determining
    # who has what piece and whose turn it will
    # be to start the game.
    game_over = False

    if random.randint(0, 1) == 0:
        print('Computer has X and will go first...')
        user = 'O'
        computer = 'X'
        whose_turn = 'computer'
    else:
        print('User has X and will go first...')
        user = 'X'
        computer = 'O'
        whose_turn = 'user'

    # We'll stay in this loop until the game is over due to someone winning
    # or until it's declared a draw.
    while not game_over:
        if whose_turn == 'user':  # Human player's turn
            print('It\'s your turn...')
            get_user_move(game_board, user)
            display_board(game_board)
            if has_player_won(game_board, user):
                print('Congrats, you won!')
                game_over = True
            else:
                whose_turn = 'computer'
        else:  # Computer's turn
            print('Computer\'s turn...')
            get_computer_move(game_board, computer)
            display_board(game_board)
            if has_player_won(game_board, computer):
                print('Sorry, the computer won :-(')
                game_over = True
            else:
                whose_turn = 'user'

        # Check to see if the game is over without a winner
        if not game_over and is_draw(game_board):
            print('Lame, it\'s a draw :-\.')
            game_over = True
        print('===============================================================')

    print('Thanks for playing!')

def display_board(game_board):
    """This function prints the game board for the user to see its state."""
    for r in range(len(game_board)):
        for c in range(len(game_board[r])):
            # Use an empty space, or the player symbol in the placeholder.
            symbol = ' '
            if game_board[r][c]:
                symbol = game_board[r][c]
            print('_{}_'.format(symbol), end='')
            # Tuples and in are a quick way of checking
            # if a value is one of a possible set of
            # values.
            if c in (0, 1):
                print('|', end='')
            else:
                print('')

def get_user_move(game_board, symbol):
    """Ask the user for their row and column move and set the game_board with
       their symbol."""
    row = int(input('What row do you want to play? (0-2) '))
    col = int(input('What column do you want to play? (0-2) '))
    while not is_legal_move(game_board, row, col):
        print('{},{} is not a legal move'.format(row, col))
        row = int(input('What row do you want to play? (0-2) '))
        col = int(input('What column do you want to play? (0-2) '))
    game_board[row][col] = symbol

def get_computer_move(game_board, symbol):
    """Generate random number coordinates for the computer to place their
    symbol."""
    row = random.randint(0, 2)
    col = random.randint(0, 2)
    while not is_legal_move(game_board, row, col):
        row = random.randint(0, 2)
        col = random.randint(0, 2)
    game_board[row][col] = symbol

def is_legal_move(game_board, row, col):
    """Check to see if the row and col provided are within the boundaries of
       the board and if the space is unoccupied."""
    if row < 0 or row > 2:
        return False
    if col < 0 or col > 2:
        return False
    # Remember, any non-empty string evaluates to True
    # in the context of an if statement
    if game_board[row][col]:
        return False

    return True

def is_draw(game_board):
    """Determine if a game is a draw by checking each space. Once we find at
       least one empty spot, we can return False since it's a playable spot."""
    for r in range(len(game_board)):
        for c in range(len(game_board[r])):
            # An empty string evaluates to False inside
            # the context of an if statement
            if not game_board[r][c]:
                return False
    return True

def has_player_won(game_board, symbol):
    """Check to see if the given symbol has won the game in any of the possible
       ways."""

    # Remember, with sequences the multiplication
    # operator repeats the value so 'X' * 3 == 'XXX'
    winner_sequence = symbol * 3

    # Check for horizontal wins
    for r in range(len(game_board)):
        row_symbols = ''
        for c in range(len(game_board[r])):
            row_symbols += game_board[r][c]
        if row_symbols == winner_sequence:
            return True

    # Check for vertical wins
    for c in range(len(game_board[0])):
        col_symbols = ''
        for r in range(len(game_board)):
            col_symbols += game_board[r][c]
        if col_symbols == winner_sequence:
            return True

    # Check for the two diagonal wins
    # Note this will only work on a square board!
    diag_symbols = ''
    anti_diag_symbols = ''
    for rc in range(len(game_board)):
        diag_symbols += game_board[rc][rc]
        anti_diag_symbols += game_board[rc][len(game_board) - 1 - rc]
    if winner_sequence in (diag_symbols, anti_diag_symbols):
        return True

    # If we got here, nobody won yet
    return False

main()


import RBHD
import requests
import pandas as pd
from datetime import datetime
from datetime import timezone
import time

count1 = time.time()

data = RBHD.rbhd_login_and_gather()

orders = data['orders'].sort_values('Date',ascending=False).reset_index().drop('index',axis = 1)

modify_order = pd.read_csv('order_modify.csv')
modify_order['Date'] = modify_order['Date'].astype('datetime64[ns]')

orders = pd.concat([orders,modify_order]).sort_values('Date',ascending=False)

orders.to_csv("orders_cache.csv")
dividends = data['dividends']
dividends.to_csv("dividends_cache.csv")

import Quote_Gatherer

all_quote = Quote_Gatherer.return_all_quotes(orders)
price_table = Quote_Gatherer.generate_price_table(all_quote)

all_quote.to_csv('all_quote_cache.csv')
price_table.to_csv('price_table_cache.csv')

#the following section add in "underlying","split","dividend", and "option expire" items to the orders

def get_underlying(instrument):
    if instrument['Instrument_Type'] == "Option":
        return instrument["Ticker"][:-15]
    else:
        return instrument["Ticker"]

orders['Underlying'] = orders.apply(lambda instrument: get_underlying(instrument),axis = 1)

day1list = pd.DataFrame(orders.groupby('Underlying').min()['Date'])
for i in day1list.index:
    ticker_i = i
    date1 = day1list.loc[i,"Date"].strftime("%Y-%m-%d")
    date2 = datetime.today().strftime("%Y-%m-%d")
    token = open('finnhub_token.txt','r').read()
    r = requests.get('https://finnhub.io/api/v1/stock/split?symbol=' + ticker_i + '&from=' + date1 + "&to=+" + date2 + '&token=' + token)

    for j in r.json():
        orders = orders.append(pd.Series({"Ticker":ticker_i,
            "Date":j['date'],
            "Price/Share":0.0,
            "Share":0.0,
            "Transaction_Type":"SPLIT",
            "Transaction_Amount":0.0,
            "Split_Factor":j['toFactor']/j['fromFactor'],
            "Instrument_Type":"Equity",
            "Underlying":ticker_i
        }),ignore_index=True)
orders['Date'] = orders['Date'].astype('datetime64[ns]')
orders = orders.sort_values("Date").reset_index().drop("index",axis=1)

dividends['amount'] = dividends['amount'].astype('float')
dividends = dividends[dividends['state']=="paid"]
for i in dividends.index:
    div = dividends.loc[i,:]
    orders = orders.append(pd.Series({"Ticker":div['Ticker'],
        "Date":div['paid_at'],
        "Price/Share":0.0,
        "Share":0.0,
        "Transaction_Type":"DIVIDEND",
        "Transaction_Amount":div['amount'],
        "Instrument_Type":"Equity",
        "Underlying":div['Ticker']
    }),ignore_index=True)

today_date = datetime.today()
day1list_option = pd.DataFrame(orders[orders['Instrument_Type']=="Option"].groupby('Ticker').min()['Date'])
for i in day1list_option.index:
    expire_date = datetime.strptime((i[-15:-13] + i[-13:-11] + i[-11:-9] +"1730" ), '%y%m%d%H%M')
    if today_date > expire_date:
        orders = orders.append(pd.Series({"Ticker":i,
            "Date":expire_date,
            "Price/Share":0.0,
            "Share":0.0,
            "Transaction_Type":"EXPIRE",
            "Transaction_Amount":0.0,
            "Instrument_Type":"Option",
            "Underlying":i[0:-15]
        }),ignore_index=True)

orders.sort_values("Date").to_csv("all_transactions.csv")

count2 = time.time()
print("All files have been cached. Spent: " + str(round(count2 - count1,2)) + "s.")


import math
import pandas as pd
import numpy as np
from tqdm import tqdm

def result2list(tmp_value):
    """
    辅助函数，将series按照索引返回的单个值或者pd.series转换成list
    :param tmp_value:
    :return:
    """
    if (type(tmp_value) != pd.core.series.Series):
        tmp_value = np.array([tmp_value])
    else:
        tmp_value = tmp_value.values
    return tmp_value

class Metric():

    def __init__(self,type,train_UM,test_UM,Recommender,N=3,K=3):
        self.type = type
        self.train_users = set(train_UM.index.values)
        self.test_users = set(test_UM.index.values)
        self.train_um = train_UM
        self.test_um = test_UM
        self.Recommender = Recommender
        self.N = N
        self.K = K
        self.recs = self.getRec()

    # 为test中的每个用户进行推荐
    def getRec(self):
        recs = {}
        for user in tqdm(self.test_users):
            rank = self.Recommender.pred(user,self.N,self.K)
            recs[user] = rank
        return recs

    # 定义精确率指标计算方式
    def precision(self):
        all, hit = 0, 0
        for user in self.test_users:
            test_items = set(result2list(self.test_um.loc[user]))
            rank = self.recs[user]
            for item in rank:
                if item in test_items:
                    hit += 1
            all += len(rank)
        return round(hit / all * 100, 2)

    # 定义召回率指标计算方式
    def recall(self):
        all, hit = 0, 0
        for user in self.test_users:
            test_items = set(result2list(self.test_um.loc[user]))
            rank = self.recs[user]
            for item in rank:
                if item in test_items:
                    hit += 1
            all += len(test_items)
        return round(hit / all * 100, 2)

    # 定义覆盖率指标计算方式
    def coverage(self):
        all_item, recom_item = set(), set()
        for user in self.test_users:
            for item in self.train_um.loc[user]:
                all_item.add(item)
            rank = self.recs[user]
            for item in rank:
                recom_item.add(item)
        return round(len(recom_item) / len(all_item) * 100, 2)

    # 定义新颖度指标计算方式
    def popularity(self):
        # 计算物品的流行度
        item_pop = {}
        for user in self.train_users:
            for item in self.train_um.loc[user]:
                if item not in item_pop:
                    item_pop[item] = 0
                item_pop[item] += 1

        num, pop = 0, 0
        for user in self.test_users:
            rank = self.recs[user]
            for item in rank:
                # 取对数，防止因长尾问题带来的被流行物品所主导
                pop += math.log(1 + item_pop[item])
                num += 1
        return round(pop / num, 6)

    def eval(self):
        metric = {'Precision': self.precision(),
                  'Recall': self.recall(),
                  'Coverage': self.coverage(),
                  'Popularity': self.popularity()}
        print('Metric:', metric)
        return metric


#
# Copyright (c) 2019-2020, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import sys

import numpy as np
import rmm

from xbb_tools.utils import (
    benchmark,
    tpcxbb_argparser,
    train_clustering_model,
    run_dask_cudf_query,
)
from xbb_tools.readers import build_reader
from dask import delayed

try:
    cli_args = tpcxbb_argparser()
except:
    cli_args = {}

# q25 parameters
Q25_DATE = "2002-01-02"
N_CLUSTERS = 8
CLUSTER_ITERATIONS = 20
N_ITER = 5

@benchmark(dask_profile=cli_args.get("dask_profile"),)
def read_tables():
    table_reader = build_reader(basepath=cli_args["data_dir"])

    ss_cols = ["ss_customer_sk", "ss_sold_date_sk", "ss_ticket_number", "ss_net_paid"]
    ws_cols = [
        "ws_bill_customer_sk",
        "ws_sold_date_sk",
        "ws_order_number",
        "ws_net_paid",
    ]
    datedim_cols = ["d_date_sk", "d_date"]

    ss_ddf = table_reader.read("store_sales", relevant_cols=ss_cols, index=False)
    ws_ddf = table_reader.read("web_sales", relevant_cols=ws_cols, index=False)
    datedim_ddf = table_reader.read("date_dim", relevant_cols=datedim_cols, index=False)

    return (ss_ddf, ws_ddf, datedim_ddf)

def agg_count_distinct(df, group_key, counted_key, client):
    """Returns a Series that is the result of counting distinct instances of 'counted_key' within each 'group_key'.
    The series' index will have one entry per unique 'group_key' value.
    Workaround for lack of nunique aggregate function on Dask df.
    """

    ### going via repartition for split_out drop duplicates
    ### see issue: https://github.com/rapidsai/tpcx-bb-internal/issues/492
    unique_df = df[[group_key, counted_key]].map_partitions(
        lambda df: df.drop_duplicates()
    )
    unique_df = unique_df.repartition(columns=[group_key])
    unique_df = unique_df.map_partitions(lambda df: df.drop_duplicates())

    return unique_df.groupby(group_key)[counted_key].count(split_every=2)

def convert_datestring_to_days(df, date_col="d_date", date_format="%Y-%m-%d"):
    """
    Utility to convert datestring to int representing days
    """
    datetime_array = rmm.device_array(len(df), dtype=np.int64)
    df[date_col].str.timestamp2int(
        format=date_format, units="D", devptr=datetime_array.device_ctypes_pointer.value
    )
    df[date_col] = datetime_array
    return df

@benchmark(dask_profile=cli_args.get("dask_profile"))
def get_clusters(client, ml_input_df):
    ml_tasks = [
        delayed(train_clustering_model)(df, N_CLUSTERS, CLUSTER_ITERATIONS, N_ITER)
        for df in ml_input_df.to_delayed()
    ]
    results_dict = client.compute(*ml_tasks, sync=True)

    output = ml_input_df.index.to_frame().reset_index(drop=True)

    labels_final = dask_cudf.from_cudf(
        results_dict["cid_labels"], npartitions=output.npartitions
    )
    output["label"] = labels_final.reset_index()[0]

    # Based on CDH6.1 q25-result formatting
    results_dict["cid_labels"] = output
    return results_dict

@benchmark(dask_profile=cli_args.get("dask_profile"))
def main(client):
    ss_ddf, ws_ddf, datedim_ddf = read_tables()
    datedim_ddf = datedim_ddf.map_partitions(convert_datestring_to_days)
    min_date = np.datetime64(Q25_DATE, "D").astype(int)
    # Filter by date
    valid_dates_ddf = datedim_ddf[datedim_ddf["d_date"] > min_date].reset_index(
        drop=True
    )

    f_ss_ddf = ss_ddf[ss_ddf["ss_customer_sk"].notnull()].reset_index(drop=True)
    f_ws_ddf = ws_ddf[ws_ddf["ws_bill_customer_sk"].notnull()].reset_index(drop=True)

    # Merge
    ss_merged_df = f_ss_ddf.merge(
        valid_dates_ddf, left_on="ss_sold_date_sk", right_on="d_date_sk", how="inner"
    )
    ws_merged_df = f_ws_ddf.merge(
        valid_dates_ddf, left_on="ws_sold_date_sk", right_on="d_date_sk", how="inner"
    )

    # Roll up store sales
    agg_store_sales_ddf = ss_merged_df.groupby("ss_customer_sk").agg(
        {"ss_sold_date_sk": "max", "ss_net_paid": "sum"}
    )

    agg_store_sales_ddf["frequency"] = agg_count_distinct(
        ss_merged_df, "ss_customer_sk", "ss_ticket_number", client=client
    )  # Simulate count distinct

    # Same rollup, just different columns for web sales
    agg_web_sales_ddf = ws_merged_df.groupby("ws_bill_customer_sk").agg(
        {"ws_sold_date_sk": "max", "ws_net_paid": "sum"}
    )

    agg_web_sales_ddf["frequency"] = agg_count_distinct(
        ws_merged_df, "ws_bill_customer_sk", "ws_order_number", client=client
    )  # Simulate count distinct

    agg_store_sales_ddf = agg_store_sales_ddf.reset_index()
    agg_web_sales_ddf = agg_web_sales_ddf.reset_index()

    shared_columns = ["cid", "most_recent_date", "amount", "frequency"]
    agg_store_sales_ddf.columns = shared_columns
    agg_web_sales_ddf.columns = shared_columns
    agg_sales_ddf = dask_cudf.concat([agg_store_sales_ddf, agg_web_sales_ddf])

    cluster_input_ddf = agg_sales_ddf.groupby("cid").agg(
        {"most_recent_date": "max", "frequency": "sum", "amount": "sum"}
    )

    cluster_input_ddf["recency"] = (37621 - cluster_input_ddf["most_recent_date"]) < 60

    # Reorder to match refererence examples
    cluster_input_ddf = cluster_input_ddf[["recency", "frequency", "amount"]]

    # Prepare df for KMeans clustering
    cluster_input_ddf["recency"] = cluster_input_ddf["recency"].astype("int64")
    cluster_input_ddf["amount"] = cluster_input_ddf["amount"].astype("float64")

    cluster_input_ddf = cluster_input_ddf.persist()

    results_dict = get_clusters(client=client, ml_input_df=cluster_input_ddf)
    return results_dict

if __name__ == "__main__":
    from xbb_tools.cluster_startup import attach_to_cluster
    import cudf
    import dask_cudf

    client = attach_to_cluster(cli_args)

    run_dask_cudf_query(cli_args=cli_args, client=client, query_func=main)