#! /usr/bin/env python3

import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

import sys
sys.path.append('../Signal')
import Signal

if __name__ == "__main__":
    width = 3
    time_array = np.linspace(-np.pi*width, np.pi*width, 128*width)
    wave1 = Signal.Signal(time_array, 2, 5, 0)
    wave1.calculateSineWave()
    window = signal.hamming(len(wave1.getSineWave))

    conv = signal.convolve(wave1.getSineWave, window)
    conv_len = len(conv)

    print("Len Wave1 {}".format(len(wave1.getSineWave)))
    print("Len Conv {}".format(len(conv)))

    plt.figure(1)

    plt.subplot(311)
    plt.grid(True)
    plt.plot(time_array, wave1.getSineWave)

    plt.subplot(312)
    plt.grid(True)
    plt.plot(time_array, window)

    plt.subplot(313)
    plt.grid(True)
    plt.plot(time_array, conv[0:(conv_len/2)+1])

    plt.show()


import pytest, testinfra

def define_server_test(server, app_env):

    def _function():
        host = testinfra.get_host("paramiko://root@" + server, ssh_config="/root/.ssh/config")
        app_id_path = '/etc/app.id'
        assert host.file(app_id_path).exists
        assert host.file(app_id_path).content_string.strip() == f"CIC_WEBAPP:{app_env}:{server}"

    return _function

servers = {'dev-app1': 'DEV', 'dev-app2' : 'DEV', 'prod-app1' : 'PROD', 'prod-app2' : 'PROD'}

for server, app_env in servers.items():

    exec(f"test_{server.replace('-','_')}_server = define_server_test('{server}', '{app_env}')")


import itertools
from packaging.version import Version

import pytest
import numpy as np
import tensorflow as tf

from tensorflow_addons import text
from tensorflow_addons.utils import test_utils
from numpy.testing import assert_array_equal

def calculate_sequence_score(inputs, transition_params, tag_indices, sequence_lengths):
    expected_unary_score = sum(
        inputs[i][tag_indices[i]] for i in range(sequence_lengths)
    )
    expected_binary_score = sum(
        transition_params[tag_indices[i], tag_indices[i + 1]]
        for i in range(sequence_lengths - 1)
    )
    return expected_unary_score + expected_binary_score

def brute_force_decode(sequence_lengths, inputs, transition_params):
    num_words = inputs.shape[0]
    num_tags = inputs.shape[1]

    all_sequence_scores = []
    all_sequences = []

    tag_indices_iterator = itertools.product(range(num_tags), repeat=sequence_lengths)
    inputs = tf.expand_dims(inputs, 0)
    sequence_lengths = tf.expand_dims(sequence_lengths, 0)
    transition_params = tf.constant(transition_params)

    # Compare the dynamic program with brute force computation.
    for tag_indices in tag_indices_iterator:
        tag_indices = list(tag_indices)
        tag_indices.extend([0] * (num_words - sequence_lengths))
        all_sequences.append(tag_indices)
        sequence_score = text.crf_sequence_score(
            inputs=inputs,
            tag_indices=tf.expand_dims(tag_indices, 0),
            sequence_lengths=sequence_lengths,
            transition_params=transition_params,
        )
        sequence_score = tf.squeeze(sequence_score, [0])
        all_sequence_scores.append(sequence_score)

    expected_max_sequence_index = np.argmax(all_sequence_scores)
    expected_max_sequence = all_sequences[expected_max_sequence_index]
    expected_max_score = all_sequence_scores[expected_max_sequence_index]
    return expected_max_sequence, expected_max_score

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_filtered_inputs(dtype):
    # Test both the length-1 and regular cases.
    sequence_lengths_list = [np.array(3, dtype=np.int32), np.array(1, dtype=np.int32)]
    inputs_list = [
        np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype),
        np.array([[4, 5, -3]], dtype=dtype),
    ]
    tag_bitmap_list = [
        np.array(
            [
                [True, False, False],
                [False, True, True],
                [False, True, True],
                [False, True, True],
            ],
            dtype=bool,
        ),
        np.array([[False, True, True]], dtype=bool),
    ]
    neg_inf = float("-inf")
    expected_filtered_inputs_list = [
        np.array(
            [[4, neg_inf, neg_inf], [neg_inf, -1, 3], [neg_inf, 2, 1], [neg_inf, 0, 0]],
            dtype=dtype,
        ),
        np.array([[neg_inf, 5, -3]], dtype=dtype),
    ]
    for sequence_lengths, inputs, tag_bitmap, expected_filtered_inputs in zip(
        sequence_lengths_list,
        inputs_list,
        tag_bitmap_list,
        expected_filtered_inputs_list,
    ):
        filtered_inputs = text.crf_filtered_inputs(
            inputs=tf.expand_dims(inputs, 0), tag_bitmap=tf.expand_dims(tag_bitmap, 0)
        )
        filtered_inputs = tf.squeeze(filtered_inputs, [0])

        test_utils.assert_allclose_according_to_type(
            filtered_inputs, expected_filtered_inputs
        )

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_sequence_score(dtype):
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    # Test both the length-1 and regular cases.
    sequence_lengths_list = [
        np.array(3, dtype=np.int32),
        np.array(1, dtype=np.int32),
    ]
    inputs_list = [
        np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype),
        np.array([[4, 5, -3]], dtype=dtype),
    ]
    tag_indices_list = [
        np.array([1, 2, 1, 0], dtype=np.int32),
        np.array([1], dtype=np.int32),
    ]
    for sequence_lengths, inputs, tag_indices in zip(
        sequence_lengths_list, inputs_list, tag_indices_list
    ):
        sequence_score = text.crf_sequence_score(
            inputs=tf.expand_dims(inputs, 0),
            tag_indices=tf.expand_dims(tag_indices, 0),
            sequence_lengths=tf.expand_dims(sequence_lengths, 0),
            transition_params=tf.constant(transition_params),
        )
        sequence_score = tf.squeeze(sequence_score, [0])

        expected_sequence_score = calculate_sequence_score(
            inputs, transition_params, tag_indices, sequence_lengths
        )
        test_utils.assert_allclose_according_to_type(
            sequence_score, expected_sequence_score
        )

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_multi_tag_sequence_score(dtype):
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    # Test both the length-1 and regular cases.
    sequence_lengths_list = [
        np.array(3, dtype=np.int32),
        np.array(1, dtype=np.int32),
    ]
    inputs_list = [
        np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype),
        np.array([[4, 5, -3]], dtype=dtype),
    ]
    tag_bitmap_list = [
        np.array(
            [
                [True, True, False],
                [True, False, True],
                [False, True, True],
                [True, False, True],
            ],
            dtype=bool,
        ),
        np.array([[True, True, False]], dtype=bool),
    ]
    for sequence_lengths, inputs, tag_bitmap in zip(
        sequence_lengths_list, inputs_list, tag_bitmap_list
    ):
        sequence_score = text.crf_multitag_sequence_score(
            inputs=tf.expand_dims(inputs, 0),
            tag_bitmap=tf.expand_dims(tag_bitmap, 0),
            sequence_lengths=tf.expand_dims(sequence_lengths, 0),
            transition_params=tf.constant(transition_params),
        )
        sequence_score = tf.squeeze(sequence_score, [0])
        all_indices_list = [
            single_index_bitmap.nonzero()[0]
            for single_index_bitmap in tag_bitmap[:sequence_lengths]
        ]
        expected_sequence_scores = [
            calculate_sequence_score(
                inputs, transition_params, indices, sequence_lengths
            )
            for indices in itertools.product(*all_indices_list)
        ]
        expected_log_sum_exp_sequence_scores = np.logaddexp.reduce(
            expected_sequence_scores
        )
        test_utils.assert_allclose_according_to_type(
            sequence_score, expected_log_sum_exp_sequence_scores
        )

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_unary_score(dtype):
    inputs = np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype)
    for dtype in (np.int32, np.int64):
        tag_indices = np.array([1, 2, 1, 0], dtype=dtype)
        sequence_lengths = np.array(3, dtype=np.int32)
        unary_score = text.crf_unary_score(
            tag_indices=tf.expand_dims(tag_indices, 0),
            sequence_lengths=tf.expand_dims(sequence_lengths, 0),
            inputs=tf.expand_dims(inputs, 0),
        )
        unary_score = tf.squeeze(unary_score, [0])
        expected_unary_score = sum(
            inputs[i][tag_indices[i]] for i in range(sequence_lengths)
        )
        test_utils.assert_allclose_according_to_type(unary_score, expected_unary_score)

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_binary_score(dtype):
    tag_indices = np.array([1, 2, 1, 0], dtype=np.int32)
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    sequence_lengths = np.array(3, dtype=np.int32)
    binary_score = text.crf_binary_score(
        tag_indices=tf.expand_dims(tag_indices, 0),
        sequence_lengths=tf.expand_dims(sequence_lengths, 0),
        transition_params=tf.constant(transition_params),
    )
    binary_score = tf.squeeze(binary_score, [0])
    expected_binary_score = sum(
        transition_params[tag_indices[i], tag_indices[i + 1]]
        for i in range(sequence_lengths - 1)
    )
    test_utils.assert_allclose_according_to_type(binary_score, expected_binary_score)

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_log_norm(dtype):
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    # Test both the length-1 and regular cases.
    sequence_lengths_list = [
        np.array(3, dtype=np.int32),
        np.array(1, dtype=np.int64),
    ]
    inputs_list = [
        np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype),
        np.array([[3, -1, 3]], dtype=dtype),
    ]
    tag_indices_list = [
        np.array([1, 2, 1, 0], dtype=np.int32),
        np.array([2], dtype=np.int32),
    ]

    for sequence_lengths, inputs, tag_indices in zip(
        sequence_lengths_list, inputs_list, tag_indices_list
    ):
        num_words = inputs.shape[0]
        num_tags = inputs.shape[1]
        all_sequence_scores = []

        # Compare the dynamic program with brute force computation.
        for tag_indices in itertools.product(range(num_tags), repeat=sequence_lengths):
            tag_indices = list(tag_indices)
            tag_indices.extend([0] * (num_words - sequence_lengths))
            all_sequence_scores.append(
                text.crf_sequence_score(
                    inputs=tf.expand_dims(inputs, 0),
                    tag_indices=tf.expand_dims(tag_indices, 0),
                    sequence_lengths=tf.expand_dims(sequence_lengths, 0),
                    transition_params=tf.constant(transition_params),
                )
            )

        brute_force_log_norm = tf.reduce_logsumexp(all_sequence_scores)
        log_norm = text.crf_log_norm(
            inputs=tf.expand_dims(inputs, 0),
            sequence_lengths=tf.expand_dims(sequence_lengths, 0),
            transition_params=tf.constant(transition_params),
        )
        log_norm = tf.squeeze(log_norm, [0])

        test_utils.assert_allclose_according_to_type(log_norm, brute_force_log_norm)

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_log_norm_zero_seq_length(dtype):
    """Test `crf_log_norm` when `sequence_lengths` contains one or more
    zeros."""
    inputs = tf.constant(np.ones([2, 10, 5], dtype=dtype))
    transition_params = tf.constant(np.ones([5, 5], dtype=dtype))
    sequence_lengths = tf.constant(np.zeros([2], dtype=np.int32))
    expected_log_norm = np.zeros([2], dtype=dtype)
    log_norm = text.crf_log_norm(inputs, sequence_lengths, transition_params)
    test_utils.assert_allclose_according_to_type(log_norm, expected_log_norm)

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_log_likelihood(dtype):
    inputs = np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype)
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    sequence_lengths = np.array(3, dtype=np.int32)

    num_words = inputs.shape[0]
    num_tags = inputs.shape[1]
    all_sequence_log_likelihoods = []

    # Make sure all probabilities sum to 1.
    for tag_indices in itertools.product(range(num_tags), repeat=sequence_lengths):
        tag_indices = list(tag_indices)
        tag_indices.extend([0] * (num_words - sequence_lengths))
        sequence_log_likelihood, _ = text.crf_log_likelihood(
            inputs=tf.expand_dims(inputs, 0),
            tag_indices=tf.expand_dims(tag_indices, 0),
            sequence_lengths=tf.expand_dims(sequence_lengths, 0),
            transition_params=tf.constant(transition_params),
        )
        all_sequence_log_likelihoods.append(sequence_log_likelihood)
    total_log_likelihood = tf.reduce_logsumexp(all_sequence_log_likelihoods)
    test_utils.assert_allclose_according_to_type(
        total_log_likelihood, 0.0, rtol=1e-6, atol=1e-6, half_rtol=2e-3, half_atol=2e-3
    )

    # check if `transition_params = None` raises an error
    text.crf_log_likelihood(
        inputs=tf.expand_dims(inputs, 0),
        tag_indices=tf.expand_dims(tag_indices, 0),
        sequence_lengths=tf.expand_dims(sequence_lengths, 0),
    )

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_viterbi_decode(dtype):
    inputs = np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype)
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    sequence_lengths = np.array(3, dtype=np.int32)
    num_words = inputs.shape[0]
    num_tags = inputs.shape[1]

    all_sequence_scores = []
    all_sequences = []

    # Compare the dynamic program with brute force computation.
    for tag_indices in itertools.product(range(num_tags), repeat=sequence_lengths):
        tag_indices = list(tag_indices)
        tag_indices.extend([0] * (num_words - sequence_lengths))
        all_sequences.append(tag_indices)
        sequence_score = text.crf_sequence_score(
            inputs=tf.expand_dims(inputs, 0),
            tag_indices=tf.expand_dims(tag_indices, 0),
            sequence_lengths=tf.expand_dims(sequence_lengths, 0),
            transition_params=tf.constant(transition_params),
        )
        sequence_score = tf.squeeze(sequence_score, [0])
        all_sequence_scores.append(sequence_score)

    expected_max_sequence_index = np.argmax(all_sequence_scores)
    expected_max_sequence = all_sequences[expected_max_sequence_index]
    expected_max_score = all_sequence_scores[expected_max_sequence_index]

    actual_max_sequence, actual_max_score = text.viterbi_decode(
        inputs[:sequence_lengths], transition_params
    )

    test_utils.assert_allclose_according_to_type(actual_max_score, expected_max_score)
    assert actual_max_sequence == expected_max_sequence[:sequence_lengths]

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_decode(dtype):
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    # Test both the length-1 and regular cases.
    sequence_lengths_list = [
        np.array(3, dtype=np.int32),
        np.array(1, dtype=np.int64),
    ]
    inputs_list = [
        np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype),
        np.array([[-1, 2, 1]], dtype=dtype),
    ]
    tag_indices_list = [
        np.array([1, 2, 1, 0], dtype=np.int32),
        np.array([2], dtype=np.int32),
    ]

    for sequence_lengths, inputs, tag_indices in zip(
        sequence_lengths_list, inputs_list, tag_indices_list
    ):
        expected_max_sequence, expected_max_score = brute_force_decode(
            sequence_lengths, inputs, transition_params
        )

        actual_max_sequence, actual_max_score = text.crf_decode(
            tf.expand_dims(inputs, 0),
            tf.constant(transition_params),
            tf.expand_dims(sequence_lengths, 0),
        )
        actual_max_sequence = tf.squeeze(actual_max_sequence, [0])
        actual_max_score = tf.squeeze(actual_max_score, [0])

        test_utils.assert_allclose_according_to_type(
            actual_max_score, expected_max_score, 1e-6, 1e-6
        )
        assert (
            list(actual_max_sequence[:sequence_lengths])
            == expected_max_sequence[:sequence_lengths]
        )

@pytest.mark.parametrize("dtype", [np.float16, np.float32])
def test_crf_constrained_decode(dtype):
    transition_params = np.array([[-3, 5, -2], [3, 4, 1], [1, 2, 1]], dtype=dtype)
    # Test both the length-1 and regular cases.
    sequence_lengths_list = [np.array(3, dtype=np.int32), np.array(1, dtype=np.int32)]
    inputs_list = [
        np.array([[4, 5, -3], [3, -1, 3], [-1, 2, 1], [0, 0, 0]], dtype=dtype),
        np.array([[4, 5, -3]], dtype=dtype),
    ]
    tag_bitmap_list = [
        np.array(
            [
                [True, False, False],
                [False, True, True],
                [False, True, True],
                [False, True, True],
            ],
            dtype=bool,
        ),
        np.array([[False, True, True]], dtype=bool),
    ]
    for sequence_lengths, inputs, tag_bitmap in zip(
        sequence_lengths_list, inputs_list, tag_bitmap_list
    ):
        filtered_inputs = text.crf_filtered_inputs(
            inputs=tf.expand_dims(inputs, 0), tag_bitmap=tf.expand_dims(tag_bitmap, 0)
        )

        expected_max_sequence, expected_max_score = text.crf_decode(
            filtered_inputs,
            tf.constant(transition_params),
            tf.expand_dims(sequence_lengths, 0),
        )

        expected_max_sequence = tf.squeeze(expected_max_sequence, [0])
        expected_max_score = tf.squeeze(expected_max_score, [0])

        actual_max_sequence, actual_max_score = text.crf_constrained_decode(
            tf.expand_dims(inputs, 0),
            tf.expand_dims(tag_bitmap, 0),
            tf.constant(transition_params),
            tf.expand_dims(sequence_lengths, 0),
        )

        actual_max_sequence = tf.squeeze(actual_max_sequence, [0])
        actual_max_score = tf.squeeze(actual_max_score, [0])

        test_utils.assert_allclose_according_to_type(
            actual_max_score, expected_max_score, 1e-6, 1e-6
        )
        assert list(actual_max_sequence[:sequence_lengths]) == list(
            expected_max_sequence[:sequence_lengths]
        )

def test_crf_decode_zero_seq_length():
    """Test that crf_decode works when sequence_length contains one or more
    zeros."""
    inputs = tf.constant(np.ones([2, 10, 5], dtype=np.float32))
    transition_params = tf.constant(np.ones([5, 5], dtype=np.float32))
    sequence_lengths = tf.constant(np.zeros([2], dtype=np.int32))
    tags, scores = text.crf_decode(inputs, transition_params, sequence_lengths)
    assert len(tags.shape) == 2
    assert len(scores.shape) == 1

def test_different_dtype():
    inputs = np.ones([16, 20, 5], dtype=np.float32)
    tags = tf.convert_to_tensor(np.ones([16, 20], dtype=np.int64))
    seq_lens = np.ones([16], dtype=np.int64) * 20

    loss, _ = text.crf_log_likelihood(
        inputs=inputs, tag_indices=tags, sequence_lengths=seq_lens
    )

def test_tf_function():
    batch_size = 4
    num_tags = 10
    input_signature = (
        tf.TensorSpec([None, None, num_tags]),
        tf.TensorSpec([num_tags, num_tags]),
        tf.TensorSpec([None], dtype=tf.int32),
    )
    crf_decode = tf.function(input_signature=input_signature)(text.crf_decode)
    potentials = tf.random.uniform([batch_size, 1, num_tags])
    transition_params = tf.random.uniform([num_tags, num_tags])
    sequence_length = tf.ones([batch_size], dtype=tf.int32)
    crf_decode(potentials, transition_params, sequence_length)

class CRFDecode(tf.keras.layers.Layer):
    def __init__(self):
        super().__init__()

    def call(self, potentials, transition_params, sequence_length):
        return text.crf_decode(potentials, transition_params, sequence_length)

@pytest.mark.skipif(
    tf.__version__[:3] == "2.4",
    reason="CRF Decode doesn't work in TF2.4, the issue was fixed in TF core, but didn't make the release",
)
def test_crf_decode_save_load(tmpdir):
    class DummyLoss(tf.keras.losses.Loss):
        def call(self, y_true, y_pred):
            return tf.zeros(shape=())

    tf.keras.backend.clear_session()
    input_tensor = tf.keras.Input(shape=(10, 3), dtype=tf.float32, name="input_tensor")
    seq_len = tf.keras.Input(shape=(), dtype=tf.int32, name="seq_len")
    transition = tf.constant([[1, 1, 0], [0, 1, 1], [1, 0, 1]], dtype=tf.float32)

    output = tf.multiply(input_tensor, tf.constant(1.0))
    decoded, _ = CRFDecode()(input_tensor, transition, seq_len)

    model = tf.keras.Model(
        inputs=[input_tensor, seq_len], outputs=[output, decoded], name="example_model"
    )
    model.compile(optimizer="Adam", loss=DummyLoss())

    x_data = {
        "input_tensor": np.random.random_sample((5, 10, 3)).astype(dtype=np.float32),
        "seq_len": np.array([10] * 5, dtype=np.int32),
    }

    tensor_name = (
        "tf.math.multiply"
        if Version(tf.__version__) >= Version("2.5.0")
        else "tf_op_layer_Mul"
    )
    y_data = {tensor_name: np.random.randint(0, 3, (5, 10))}

    model.fit(x_data, y_data)
    model.predict(
        {
            "input_tensor": tf.expand_dims(x_data["input_tensor"][0], 0),
            "seq_len": np.array([10]),
        }
    )

    temp_dir = str(tmpdir.mkdir("model"))
    model.save(temp_dir)

    tf.keras.backend.clear_session()
    model = tf.keras.models.load_model(
        temp_dir,
        custom_objects={
            "CrfDecodeForwardRnnCell": text.crf.CrfDecodeForwardRnnCell,
            "DummyLoss": DummyLoss,
        },
    )
    model.fit(x_data, y_data)
    model.predict(
        {
            "input_tensor": tf.expand_dims(x_data["input_tensor"][0], 0),
            "seq_len": np.array([10]),
        }
    )

@pytest.mark.parametrize(
    "potentials,sequence_length",
    [
        # performs masking
        pytest.param(
            tf.random.normal([2, 12, 3]),
            tf.constant([8, 10]),
        ),
        # does not perform masking
        pytest.param(
            tf.random.normal([4, 8, 10]),
            tf.constant([8, 8, 8, 8]),
        ),
    ],
)
def test_crf_decode_forward_mask(potentials, sequence_length):
    # mimics setup of the `_multi_seq_fn` closure in `crf_decode`
    initial_state = tf.slice(potentials, [0, 0, 0], [-1, 1, -1])
    initial_state = tf.squeeze(initial_state, axis=[1])
    inputs = tf.slice(potentials, [0, 1, 0], [-1, -1, -1])

    sequence_length_less_one = tf.maximum(
        tf.constant(0, dtype=tf.int32), sequence_length - 1
    )

    n_tags = potentials.shape[-1]
    transition_params = tf.random.normal([n_tags, n_tags])

    backpointers, _ = text.crf_decode_forward(
        inputs, initial_state, transition_params, sequence_length_less_one
    )

    # everything masked by `sequence_length_less_one` should be equal to 0.
    mask = tf.sequence_mask(sequence_length_less_one, tf.shape(inputs)[1])

    # the indices that _should_ have been masked in the RNN operation
    masked_indices = tf.cast(tf.logical_not(mask), tf.int32)

    # sum of each row in the mask should equal timedim - seq lens
    exp_mask_sums = (
        tf.repeat(inputs.shape[1], inputs.shape[0]) - sequence_length_less_one
    )
    mask_sums = tf.reduce_sum(masked_indices, axis=1)
    assert_array_equal(
        exp_mask_sums.numpy(),
        mask_sums.numpy(),
    )

    # now apply the inverse mask to the backpointers and show that ALL are zeros. this is proof that
    # we appropriately masked timesteps
    masked_indices = tf.expand_dims(masked_indices, [2])
    zeros = masked_indices * backpointers
    assert tf.reduce_all(zeros == 0).numpy(), "Mask not applied correctly: {0}".format(
        zeros
    )


import unittest
from PngGenerator import PngBuilder, ColorType
from tkinter import PhotoImage, Tk

class PngBuilderRGBTestCase(unittest.TestCase):

    def setUp(self) -> None:
        self.imgData = []

        row =0
        while row < 32:
            col = 0
            rowData = []
            while col < 32:
                rowData.append([255,255,255])
                col+=1
            self.imgData.append(rowData)
            row+=1

        self.photoBuilder = PngBuilder(32,32,ColorType.RGB)
        self.photoBuilder.addIDATChunk(self.imgData)

    def test_pngMagicNumberCompliant(self):

        data=self.photoBuilder.getFileByteContent()
        self.assertEqual(data[0:8], b'\x89PNG\r\n\x1a\n')

    def test_structurePngOK(self):
        testApp = Tk()
        testApp.withdraw()
        PhotoImage(master=testApp,data= self.photoBuilder.getFileByteContent())


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('authentication', '0004_auto_20151030_1838'),
    ]

    operations = [
        migrations.AlterField(
            model_name='userimage',
            name='image',
            field=models.ImageField(max_length=300, upload_to=b'static/media/images'),
        ),
    ]


import sys
import datetime
import json
import requests
import time
import cloudscraper
from concurrent.futures import ThreadPoolExecutor
from lib2to3.pgen2 import token
from unittest import result
import os
# import pandas as pd

import bingx.Account_api as Account
import bingx.Funding_api as Funding
import bingx.Market_api as Market
import bingx.Public_api as Public
import bingx.Trade_api as Trade
import bingx.subAccount_api as SubAccount
import bingx.status_api as Status

import logging
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
# from decouple import config

scraper = cloudscraper.create_scraper()
path_file = os.path.dirname(os.path.abspath(__file__))

flag = '0'

class BINGX_FUNCTION:
    def __init__(self, keypass=None):
        print("Init")
        if keypass != None:

            # NHẬP KEY, SECRET của API
            self.api_key = ''
            self.api_secret = ''

            self.FundingAPI = Funding.FundingAPI(self.api_key, self.api_secret)
            self.TradeAPI = Trade.TradeAPI(self.api_key, self.api_secret)
            self.AccountAPI = Account.AccountAPI(self.api_key, self.api_secret)
            self.MarketAPI = Market.MarketAPI(self.api_key, self.api_secret)
            print("Init Done")

    # Tìm xem 1 số có bao nhiêu số sau dấu phẩy
    def get_decimal_token_cefi(self, number):
        string_number = str(number)
        if "." in string_number:
            decimal_places = len(string_number) - string_number.index('.') - 1
            # print(decimal_places)
        else:
            decimal_places = 0
            # print(decimal_places)
        return decimal_places

    # Chuyển 1 số thành dạng tối đa có 3 số sau dấu phẩy
    def convert_number_to_smaller(self, number):
        decimal_places = self.get_decimal_token_cefi(number)
        if decimal_places == 0:
            return number
        if decimal_places > 3:
            decimal_places = 3
        number = int(float(number)*(10**int(decimal_places))) / \
            (10**int(decimal_places))
        return number

    def get_balances_bingx(self, currency):  # Check số dư của 1 token trên sàn
        while True:
            try:
                res = self.FundingAPI.get_balances()
                print("res", res)
                for item in res['data']['balances']:
                    if item['asset'] == currency:
                        balance = item['free']
                        break
                break
            except:
                time.sleep(1)
                result = sys.exc_info()
                print("result", result)
                continue

        # print("balance_trading ", balance_trading)
        return self.convert_number_to_smaller(float(balance))

    # Lấy danh sách các lệnh đang được đặt trên sàn
    def get_depth_bingx(self, symbol, usd, proxy, fake_ip):
        url = f"https://open-api.bingx.com/openApi/swap/v2/quote/depth"
        params = {
            "symbol": f"{symbol}-{usd}",
            'limit': '20'
        }
        try:
            if fake_ip == True:
                proxies = {
                    'http': str(proxy),
                    'https': str(proxy)
                }
                res = requests.get(url, params=params, proxies=proxies, timeout=5).json()
            else:
                res = requests.get(url, params=params, timeout=5).json()
            if res['code'] != 0:
                return 0
            else:
                # print(res['data'])
                return res['data']
        except Exception as e:
            print("Exception: ", e)
            return 0

    # Kiểm tra nếu dùng 1 số usd thì mua được bao nhiêu đồng coin
    def get_return_buy_bingx(self, symbol, usd, amountin, proxy, fake_ip):
        result = self.get_depth_bingx(symbol, usd, proxy, fake_ip)
        # print("result ", result)
        try:
            list_asks = result['asks']
            print("list_buys ", list_asks)
        except:
            return 0
        sum_value_ask = 0
        total_volume = 0
        for ask in list_asks:
            # print(ask)
            sum_value_ask = sum_value_ask + float(ask[0])*float(ask[1])
            total_volume = total_volume + float(ask[1])
            if float(sum_value_ask) >= float(amountin):
                # print(ask)
                tien_con_thieu = amountin - \
                    (sum_value_ask - float(ask[0])*float(ask[1]))
                print("tien_con_thieu ", tien_con_thieu)
                total_return = total_volume - \
                    float(ask[1]) + tien_con_thieu/float(ask[0])
                print("total_return", total_return)
                return float(total_return)*(100-0.1)/100
        if float(sum_value_ask) < float(amountin):
            return (total_volume)*(100-0.26)/100

    # Kiểm tra nếu dùng 1 số coin thì bán ra được bao nhiêu đồng usd
    def get_return_sell_bingx(self, symbol, usd, amountin, proxy, fake_ip):
        result = self.get_depth_bingx(symbol, usd, proxy, fake_ip)
        try:
            list_bids = result['bids']
        except:
            return 0
        sum_value_bids = 0
        total_volume = 0
        for bid in list_bids:
            sum_value_bids = sum_value_bids + float(bid[0])*float(bid[1])
            total_volume = total_volume + float(bid[1])
            # print("sum_value_bids", sum_value_bids)
            # print("total_volume", total_volume)
            # print("------------")
            if float(total_volume) >= float(amountin):
                # print(bid)
                tien_con_thieu = amountin - (total_volume - float(bid[1]))
                # print("tien_con_thieu", tien_con_thieu)
                total_return = sum_value_bids - \
                    float(bid[0])*float(bid[1]) + tien_con_thieu*float(bid[0])
                # print("total_return", total_return)
                return float(total_return)*(100-0.26)/100
        if float(total_volume) < float(amountin):
            return float(sum_value_bids)*(100-0.26)/100

    # Tìm giá khớp lệnh cuối cùng, và lượng token có thể nhận được khi mua
    def find_quantity_price_buy_bingx(self, symbol, amountin, token_usd, proxy, fake_ip, truotgiasan):
        result = self.get_depth_bingx(symbol, token_usd, proxy, fake_ip)
        # print(result)
        try:
            list_asks = result['asks']
        except:
            return 0, 0
        # print(list_bids)
        # print(list_asks)
        sum_value_ask = 0
        total_volume = 0
        price_start = float(list_asks[0][0])
        for ask in list_asks:
            sum_value_ask = sum_value_ask + float(ask[0])*float(ask[1])
            total_volume = total_volume + float(ask[1])
            print("sum_value_ask", sum_value_ask)
            print("total_volume", total_volume)
            print("------------")
            price_find = float(ask[0])
            if float(sum_value_ask) >= float(amountin):
                # print(ask)
                tien_con_thieu = float(
                    amountin) - (float(sum_value_ask) - float(ask[0])*float(ask[1]))
                print("tien_con_thieu", tien_con_thieu)
                total_return = float(total_volume) - \
                    float(ask[1]) + tien_con_thieu/float(ask[0])
                print("total_return", total_return)
                return price_find, total_return*(100-0.1)/100
        if float(price_find) > price_start*(1+float(truotgiasan)/100):
            print("SOS bingx -buy " + str(symbol) +
                  str(price_find)+" " + str(price_start))
            return 0, 0
        print("price OK bingx price_start" +
              str(price_start) + "price_find " + (price_find))

        if float(sum_value_ask) < float(amountin):
            # 0.1 là phí giao dịch của bingx
            return price_find, total_volume*(100-0.26)/100

    # lệnh buy cần tính chuẩn với khối lượng 1000 usdt mua
    # Hàm mua theo limit
    def real_buy_in_bingx(self, token_name, token_usd, amounin, amoutoutmin, proxy, fake_ip, truotgiasan):
        symbol=f"{token_name}-{token_usd}"
        price, quantity = self.find_quantity_price_buy_bingx(symbol=token_name, amountin=amounin, token_usd=token_usd, proxy=proxy, fake_ip=fake_ip, truotgiasan=truotgiasan)
        if "e" in str(price):
            price = f'{price:.10f}'
        else:
            price = price
        print("price", price)
        print("quantity", quantity)
        if quantity == 0:
            print("Do truot gia cua san qua cao")
            return "Do truot gia cua san qua cao"
        if quantity < amoutoutmin:
            print("real_buy_in_bingx quantity" + str(quantity) +
                  " < amoutoutmin" + str(amoutoutmin))
            return "Bé hơn amoutoutmin rồi!!!"
        if "e" in str(price):
            price = f'{price:.20f}'
        else:
            price = price
        Klin = int(quantity*10**3)/(10**3)
        print("Klin ", Klin)
        print("price ", price)
        result = None
        try:
            result = self.TradeAPI.place_order(symbol=symbol, type_="LIMIT", side="BUY", price=price, quantity=Klin)
            print("=== result ===", result)
        except:
            result = sys.exc_info()
            print("result", result)
            return

        if result['code'] == 0:
            order_id = result['data']['orderId']
            print("Đã đặt lệnh thành công")
        else:
            print("Lỗi rồi.....", result)
            return result

        print("order_id", order_id)
        order_details = None
        for i in range(4):
            response = self.TradeAPI.get_orders(symbol, order_id)
            order_details = response['data']
            print("get_order_details ", order_details)
            deal_price = order_details['price']
            print("deal_fund", deal_price)
            dealSize = order_details['origQty']
            print("dealSize", dealSize)
            status = order_details['status']
            print("status", status)
            if 'PENDING' in status or 'PARTIALLY_FILLED' in status:
                if i > 2:
                    print("Lệnh đang buy market còn mở")
                    result = self.TradeAPI.cancel_order(symbol, order_id)
                    print("result_cancel_buy", result)
                    if result['data']['status'] == 'CANCELED':
                        print("ĐÃ HỦY LỆNH THÀNH CÔNG!!!")
                        if deal_price == '0':
                            result = "KHÔNG MUA ĐƯỢC__ĐÃ HỦY LỆNH THÀNH CÔNG!!!"
                        else:
                            result = "1 Phần. Nhận " + str(dealSize) + "Hết =" + str(
                                float(dealSize)*float(deal_price)) + " ĐÃ HỦY LỆNH THÀNH CÔNG"
                    else:
                        result = "HỦY LỆNH THẤT BẠI!!! Vào Hủy tay đi chị đẹp"
                    break
            else:
                result = "MUA OK. Nhận " + \
                    str(dealSize) + "Hết =" + \
                    str(float(dealSize)*float(deal_price))
                break
            time.sleep(1)
        return result

    # Hàm bán token theo limit
    def real_sell_in_bingx(self, token_name, token_usd, amounin, amoutoutmin, proxy, fake_ip, truotgiasan):
        symbol=f"{token_name}-{token_usd}"

        price, quantity = self.find_quantity_price_sell_bingx(
            symbol=token_name, amountin=amounin, token_usd=token_usd, proxy=proxy, fake_ip=fake_ip, truotgiasan=truotgiasan)
        if "e" in str(price):
            price = f'{price:.20f}'
        else:
            price = price
        print("price", price)
        print("quantity", quantity)
        if quantity == 0:
            print("Do truot gia cua san qua cao")
            return "Do truot gia cua san qua cao"
        if quantity < amoutoutmin:
            print("real_sell_in_bingx quantity" + str(quantity) +
                  " < amoutoutmin" + str(amoutoutmin))
            return "Bé hơn amoutoutmin rồi!!!"

        Klin = int(amounin*10**4)/(10**4)
        print("khối lượng vào", Klin)
        try:
            result = self.TradeAPI.place_order(symbol=symbol, type_="LIMIT", side="SELL", price=price, quantity=Klin)
        except:
            print("Lỗi ", sys.exc_info())

        if result['code'] == 0:
            order_id = result['data']['orderId']
            print("Đã đặt lệnh thành công")
        else:
            print("Lỗi rồi.....", result)
            return result

        print("order_id", order_id)
        order_details = None
        for i in range(4):
            response = self.TradeAPI.get_orders(symbol, order_id)
            order_details = response['data']
            print("get_order_details ", order_details)
            deal_price = order_details['price']
            print("deal_fund", deal_price)
            dealSize = order_details['origQty']
            print("dealSize", dealSize)
            status = order_details['status']
            print("status", status)
            if 'PENDING' in status or 'PARTIALLY_FILLED' in status:
                if i > 2:
                    print("Lệnh sell đang còn mở")
                    result = self.TradeAPI.cancel_order(symbol, order_id)
                    print("result_cancel_buy", result)
                    if result['data']['status'] == 'CANCELED':
                        print("ĐÃ HỦY LỆNH THÀNH CÔNG!!!")
                        if deal_price == '0':
                            result = "KHÔNG BÁN ĐƯỢC__ĐÃ HỦY LỆNH THÀNH CÔNG!!!"
                        else:
                            result = "Bán 1 Phần " + str(dealSize) + "Nhận được =" + str(
                                float(dealSize)*float(deal_price)) + " ĐÃ HỦY LỆNH THÀNH CÔNG"
                    else:
                        result = "HỦY LỆNH THẤT BẠI!!! Vào Hủy tay đi chị đẹp"
                    break
            else:
                result = f"THÀNH CÔNG. BÁN {Klin} {token_name} Nhận được {str(float(dealSize)*float(deal_price))} "
                break
            time.sleep(1)
        return result

    # Tìm giá khơp lệnh cuối cùng và số tiền nhận được khi bán token
    def find_quantity_price_sell_bingx(self, symbol, amountin, token_usd, proxy, fake_ip, truotgiasan):
        result = self.get_depth_bingx(symbol, token_usd, proxy, fake_ip)
        # print(f"get_depth_bingx {result}")
        try:
            list_bids = result['bids']
        except:
            return 0
        sum_value_bids = 0
        total_volume = 0
        price_start = float(list_bids[0][1])

        for bid in list_bids:
            sum_value_bids = sum_value_bids + \
                float(bid[0])*float(bid[1])  # tiền
            total_volume = total_volume + float(bid[1])  # khối lượng
            print("sum_value_bids", sum_value_bids)
            print("total_volume", total_volume)
            print("------------")
            price_find = bid[0]

            if float(total_volume) >= float(amountin):
                tien_con_thieu = amountin - (total_volume - float(bid[1]))
                print("tien_con_thieu", tien_con_thieu)
                total_return = sum_value_bids - \
                    float(bid[0])*float(bid[1]) + tien_con_thieu*float(bid[0])
                print("total_return", total_return)
                return price_find, total_return*(100-0.1)/100
            # print("price", price_find )
        if float(price_find) < price_start/(1+float(truotgiasan)/100):
            print("SOS " + str(price_find)+" " + str(price_start))
            return 10000000, 0
        print("price OK bingx" + str(price_start) +
              "price_find " + (price_find))

        if float(total_volume) < float(amountin):
            return price_find, sum_value_bids * (100-0.26)/100

    def get_chain_token(self, aset, proxy="", fake_ip=False):
        nonce = str(int(time.time() * 1000))
        res = self.FundingAPI.get_deposit_method_token(
            nonce=nonce, token=aset, proxy=proxy, fake_ip=fake_ip)
        print("res", res)
        data = res['result']
        list_chain = []
        for add in data:
            print(add)
            chain = add["method"]
            if chain not in list_chain:
                print("chain", chain)
                list_chain.append(chain)
                print("................")
        return list_chain

    # Lấy địa chỉ nạp tiền lên bingx
    def get_deposit_address_bingx(self, currency, chain):
        currency = currency.upper()
        if chain == "Polygon":
            chainID = "MATIC"
        elif chain == "OPT":
            chainID = "OPTIMISM"
        elif chain == "TRON":
            chainID = "TRX"
        elif chain == "AVAX":
            chainID = "AVAX"
        elif chain == "ETH":
            chainID = "ARBI"
        elif chain == "FTM":
            chainID = "Fantom"
        elif chain == "SOL":
            chainID = "SOL"
        elif chain == "KLAY":
            chainID = "Klaytn"
        elif chain == "ARB":
            chainID = "Arbitrum"
        else:
            chainID = chain
        print("chainID", chainID)
        currencyBinding = None
        try:
            res = self.FundingAPI.get_list_deposit_address(currency=currency)

            if len(res) == 0:
                return ["No address avaiable"]
            for re in res:
                if re['providerName'] == chainID:
                    currencyBinding = re['id']
            res = self.FundingAPI.get_deposit_address(currencyBinding=currencyBinding)
            depositAddress = res['depositAccount']['address']
            print(f"=== res: {depositAddress}")
            return depositAddress
        except:
            err = str(sys.exc_info())
            print("err", err)
            print("Kiểm tra lại Chain đi người đẹp!")
            return ["No address avaiable"]

    # Lấy trạng thái khả dụng hay bị dừng nạp tiền của 1 token
    def get_status_deposit_bingx(self, currency):
        currency = currency.upper()
        try:
            res = self.FundingAPI.get_currency()
            for item in res['data']['currencies']:
                if currency == item['currency']:
                    # print(item['supportDeposit'])
                    if item['deposit_enabled'] != True:
                        print("Tạm dừng nạp tiền rồi. Token ", currency)
                        return None
                    else:
                        return "Nạp tiền bình thường. Token " + str(currency)
        except Exception as e:
            print(f"lỗi request {e}")

    # Lấy trạng thái khả dụng hay bị dừng rút tiền
    def get_status_withdrawal_bingx(self, currency):
        currency = currency.upper()
        try:
            res = self.FundingAPI.get_currency()
            for item in res['data']['currencies']:
                if currency == item['currency']:
                    # print(item['supportWithdraw'])
                    status = item['withdraw_enabled']
                    if status != True:
                        print("Tạm dừng rút tiền rồi ", currency)
                        return None
                    else:
                        minWithdrawSingle = item['withdraw_minsize']
                        return status, minWithdrawSingle
        except Exception as e:
            print(f"lỗi request {e}")

    # Lấy lịch sử nạp tiền
    def get_deposit_history_bingx(self, coin):
        res = self.FundingAPI.get_deposit_history(coin=coin)
        print("res", res)
        try:
            for item in res:
                status = item['status']
                print("status", status)
                if status == 1:
                    print(f"Deposit {item['amount']} {coin}, ID: {item['txId']}")
                elif status == 0:
                    print(f"Credited but cannot withdraw {coin}, ID: {item['txId']}")
                elif status == 6:
                    print(f"Pending {coin}, ID: {item['txId']}")
        except:
            print("Lỗi get status deposit bingx")
            return "Lỗi get status deposit bingx"

    # Lấy lịch sử rút tiền
    def get_withdraw_history_bingx(self, coin):
        res = self.FundingAPI.get_withdraw_history(coin=coin)
        print("res", res)
        try:
            for item in res:
                status = item['status']
                print("status", status)
                if status == 0:
                    print(f"Confirmation Email has been sent {item['amount']} {coin}, ID: {item['txId']}")
                elif status == 2:
                    print(f"Waiting for confirmation {coin}, ID: {item['txId']}")
                elif status == 3:
                    print(f"Rejected {coin}, ID: {item['txId']}")
                elif status == 4:
                    print(f"Processing {coin}, ID: {item['txId']}")
                elif status == 5:
                    print(f"Withdrawal transaction failed {coin}, ID: {item['txId']}")
                elif status == 5:
                    print(f"Withdrawal completed {coin}, ID: {item['txId']}")
        except:
            print("Lỗi get status withdrawal bingx")
            return "Lỗi get status withdrawal bingx"

    # Hàm rút tiền từ bingx về  ví metamask
    def submit_token_withdrawal_bingx(self, coin, network, amount, address, walletType):
        balance = self.get_balances_bingx(coin)
        status = ''
        if float(balance) > 0 and float(balance) >= float(amount):
            try:
                print("size ", amount)
                res = self.FundingAPI.coin_withdraw(coin=coin, network=network, amount=amount, address=address, walletType=walletType)
                print("submit_token_withdrawal_bingx ", res)
                if res['data']['id']:
                    withdrawal_ID = res['data']['id']
                    print("withdrawal_ID", withdrawal_ID)
                    print("Đã rút tiền chờ tiền về tài khoản!")
                    status = "Đã rút tiền chờ tiền về tài khoản!"
                    return True, status, withdrawal_ID
                else:
                    print("Rút tiền thất bại! " + str(res['message']))
                    status = res['message']
                    return False, status, 0
            except:
                err = str(sys.exc_info())
                print("Lỗi submit_token_withdrawal_bingx ", err)
                status = err
                return 'False1', status, 0
        else:
            print("Không đủ tiền để rút rồi người đẹp!")
            status = "Không đủ tiền rút rồi!!!"
            return False, status, 0

    # Chuyển tiền tron nội bộ sàn ( Có nhiều sàn ko cần chức năng này)
    def transfer_bingx(self, type_, asset, amount):
        try:
            res=  self.FundingAPI.funds_transfer(type_=type_, asset=asset, amount=amount)
        except:
            print("Lỗi transfer main to trading_bingx ", str(sys.exc_info()))
            return "Lỗi transfer main to trading_bingx " + str(sys.exc_info())
        if res['tranId']:
            print("chuyển tiền thành công")
            status="chuyển tiền thành công"
        else:
            print("Lỗi chuyển tiền OKX "+ str(res))
            status="Lỗi chuyển tiền OKX"+ str(res)
        return status

toolbingx = BINGX_FUNCTION(keypass='')

# toolbingx.get_depth_bingx("BTC", "USDT", "", "")

# temp = toolbingx.get_return_buy_bingx(symbol="ETH", usd="USDT", amountin=1, proxy="", fake_ip=False)
# temp = toolbingx.get_return_sell_bingx(symbol="ETH", usd="USDT", amountin=1, proxy="", fake_ip=False)

# temp = toolbingx.find_quantity_price_buy_bingx("ETH", 1, "USDT", "", "", 0.1)
# temp = toolbingx.find_quantity_price_sell_bingx("ETH", 1, "USDT", "", "", 0.1)

# temp = toolbingx.real_buy_in_bingx("MATIC", "USDT", 7, 0, "", "", 0.1)
# temp = toolbingx.real_sell_in_bingx("MATIC", "USDT", 9, 0, "", "", 0.1)

temp = toolbingx.get_deposit_address_bingx("USDT", "ERC20")
# print(toolbingx.get_status_deposit_bingx("BTC"))
# temp = toolbingx.get_status_withdrawal_bingx("USDT") # no
# temp = toolbingx.get_deposit_history_bingx("USDT")
# temp = toolbingx.get_withdraw_history_bingx("USDT")
# temp = toolbingx.get_balances_bingx("USDT")
# temp = toolbingx.submit_token_withdrawal_bingx("USDT", "Polygon", 10, "0x5a66f58a075df679e87956702c74a86dc121a79f", 1)

'''
FUND_SFUTURES
Funding Account->Standard Contract
SFUTURES_FUND
Standard Contract->Funding Account
FUND_PFUTURES
Funding Account->Perpetual Futures
PFUTURES_FUND
Perpetual Futures->Funding Account
SFUTURES_PFUTURES
Standard Contract->Perpetual Futures
PFUTURES_SFUTURES
'''
temp = toolbingx.transfer_bingx("FUND_SFUTURES", "USDT", 1)
print(temp)


from flask import Flask, render_template, request, redirect, url_for, flash, send_from_directory
from flask_session import Session
from flask_login import LoginManager, login_user, login_required, current_user, logout_user
from dao import comments_dao, follows_dao, podcast_dao, series_dao, users_dao
from models import User
from datetime import date
from werkzeug.security import generate_password_hash, check_password_hash
import os, shutil
from random import randint

app = Flask(__name__)
app.secret_key = "Xd5Y1'p(j~'ec{`Ns%Sd7Q/1ICZY$E"
app.config["SESSION_TYPE"] = "filesystem"
app.config["SESSION_PERMANENT"] = False
Session(app)

login_manager = LoginManager()
login_manager.login_view = "login"
login_manager.init_app(app)

@app.errorhandler(404)
def page_not_found(e):
    flash(e, "danger")
    return redirect(url_for("home"))

@app.route("/")
def home():
    if current_user.is_authenticated:
        series_list = series_dao.get_series(current_user.id)
        categories = series_dao.get_categories(current_user.id)
    else:
        series_list = series_dao.get_series(0)
        categories = series_dao.get_categories(0)

    return render_template("home.html", series_list=series_list, categories=categories, current_category="All", get_creator_name=users_dao.get_user_username_by_id, is_following=follows_dao.is_following)

@app.route("/category/<category>")
def home_category(category):
    if current_user.is_authenticated:
        series_list = series_dao.get_series_by_category(category, current_user.id)
        categories = series_dao.get_categories(current_user.id)
    else:
        series_list = series_dao.get_series_by_category(category, 0)
        categories = series_dao.get_categories(0)
    return render_template("home.html", series_list=series_list, categories=categories, current_category=category, get_creator_name=users_dao.get_user_username_by_id, is_following=follows_dao.is_following)

@app.route('/series/<int:series_id>')
def series(series_id):
    series_selected = series_dao.get_series_by_id(series_id)
    if not series_selected:
        flash("Series do not exist...", "danger")
        return redirect(url_for("home"))

    if current_user.is_authenticated:
        podcasts = podcast_dao.get_podcasts(series_id, current_user.id)
    else:
        podcasts = podcast_dao.get_podcasts(series_id, 0)

    return render_template("series.html", series=series_selected, podcasts=podcasts, get_creator_name=users_dao.get_user_username_by_id, is_following=follows_dao.is_following, comments=comments_dao.get_comments, str=str)

@app.route("/series/new", methods=["GET", "POST"])
@login_required
def new_series():
    if request.method == "GET":
        if not current_user.is_creator:
            flash("Only creators can create a new series...", "danger")
            return redirect(url_for("home"))

        return render_template("new-series.html")

    elif request.method == "POST":
        if not current_user.is_creator:
            flash("Only creators can create a new series...", "danger")
            return redirect(url_for("home"))

        add_series = request.form.to_dict()
        image = request.files["image"]

        # form validation
        titles = series_dao.get_series_titles()
        for title in titles:
            if title["title"].lower() == add_series["title"].lower():
                flash("This title has already been used, please choose a different one...", "warning")
                return redirect(request.referrer)

        if len(add_series["title"]) < 3:
            flash("Title is too short...", "warning")
            return redirect(url_for("new_series"))

        if add_series["title"][0] == " ":
            flash("Title cannot start with an empty space...", "warning")
            return redirect(url_for("new_series"))

        special_characters = '"!@#$%^&*()-+?_=,<>/"'
        if any(c in special_characters for c in add_series["title"]):
            flash("Title cannot contain special caracters...", "warning")
            return redirect(url_for("new_series"))

        if len(add_series["title"]) > 30:
            flash("Title is too long...", "warning")
            return redirect(url_for("new_series"))

        if len(add_series["category"]) < 3:
            flash("Category is too short...", "warning")
            return redirect(url_for("new_series"))

        if add_series["category"][0] == " ":
            flash("Category cannot start with an empty space...", "warning")
            return redirect(url_for("new_series"))

        if any(c in special_characters for c in add_series["category"]):
            flash("Title cannot contain special caracters...", "warning")
            return redirect(url_for("new_series"))

        if len(add_series["category"]) > 20:
            flash("Category is too long...", "warning")
            return redirect(url_for("new_series"))

        if len(add_series["text"]) < 100:
            flash("Description is too short...", "warning")
            return redirect(url_for("new_series"))

        if add_series["text"][0] == " ":
            flash("Description cannot start with an empty space...", "warning")
            return redirect(url_for("new_series"))

        if len(add_series["text"]) > 400:
            flash("Description is too long...", "warning")
            return redirect(url_for("new_series"))

        # handle empty date
        if add_series["date"] == "":
            add_series["date"] = date.today()

        # handle image
        if image.filename == "":
            flash("File not uploaded...", "warning")
            return redirect(url_for("new_series"))

        if "." not in image.filename or (image.filename.split(".")[1] != "jpeg" and image.filename.split(".")[1] != "jpg"):
            flash("Wrong file extension...", "warning")
            return redirect(url_for("new_series"))

        # handle creator id
        add_series["creator_id"] = current_user.id

        # save to db
        (success, id) = series_dao.add_series(add_series)

        if success:
            image.save("static/images/" + add_series["title"].lower().replace(" ", "_").replace("'", "_") + ".jpeg")
            os.mkdir("static/audio/" + str(id))
            flash("Series successfully created!", "success")
        else:
            flash("Something went wrong!", "danger")

        return redirect(url_for("home"))

@app.route("/series/<int:series_id>/edit", methods=["POST"])
@login_required
def edit_series(series_id):
    # get series data
    series_edit = series_dao.get_series_by_id(series_id)
    if not series_edit:
        flash("Series do not exist...", "danger")
        return redirect(url_for("home"))

    if not current_user.id == series_edit["creator_id"]:
        flash("Only creator can edit the series...", "danger")
        return redirect(url_for("home"))

    add_series = request.form.to_dict()
    image = request.files["image"]

    # form validation - if field not present use pre-existing data
    if add_series["title"] == "":
        add_series["title"] = series_edit["title"]
    else:
        titles = series_dao.get_series_titles()
        for title in titles:
            if title["title"] == add_series["title"]:
                flash("This title has already been used, please choose a different one...", "warning")
                return redirect(request.referrer)

        if len(add_series["title"]) < 3:
            flash("Title is too short...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if add_series["title"][0] == " ":
            flash("Title cannot start with an empty space...", "warning")
            return redirect(url_for("series", series_id=series_id))

        special_characters = '"!@#$%^&*()-+?_=,<>/"'
        if any(c in special_characters for c in add_series["title"]):
            flash("Title cannot contain special caracters...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if len(add_series["title"]) > 30:
            flash("Title is too long...", "warning")
            return redirect(url_for("series", series_id=series_id))

    if add_series["category"] == "":
        add_series["category"] = series_edit["category"]
    else:
        if len(add_series["category"]) < 3:
            flash("Category is too short...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if add_series["category"][0] == " ":
            flash("Category cannot start with an empty space...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if any(c in special_characters for c in add_series["category"]):
            flash("Title cannot contain special caracters...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if len(add_series["category"]) > 20:
            flash("Category is too long...", "warning")
            return redirect(url_for("series", series_id=series_id))

    if add_series["text"] == "":
        add_series["text"] = series_edit["text"]
    else:
        if len(add_series["text"]) < 100:
            flash("Description is too short...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if add_series["text"][0] == " ":
            flash("Description cannot start with an empty space...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if len(add_series["text"]) > 400:
            flash("Description is too long...", "warning")
            return redirect(url_for("series", series_id=series_id))

    if add_series["date"] == "":
        add_series["date"] = series_edit["date"]

    # handle image
    if image.filename != "":
        if "." not in image.filename or (image.filename.split(".")[1] != "jpeg" and image.filename.split(".")[1] != "jpg"):
            flash("Wrong file extension...", "warning")
            return redirect(url_for("series", series_id=series_id))

    add_series["creator_id"] = current_user.id
    add_series["id"] = series_edit["id"]

    success = series_dao.update_series(add_series)

    if success:
        if image.filename == "":
            os.rename("static/images/" + series_edit["title"].lower().replace(" ", "_").replace("'", "_") + ".jpeg", "static/images/" + add_series["title"].lower().replace(" ", "_").replace("'", "_") + ".jpeg")
        else:
            image.save("static/images/" + add_series["title"].lower().replace(" ", "_").replace("'", "_") + ".jpeg")
            if add_series["title"].lower() != series_edit["title"].lower():
                os.remove("static/images/" + series_edit["title"].lower().replace(" ", "_").replace("'", "_") + ".jpeg")
        flash("Series successfully updated!", "success")
    else:
        flash("Something went wrong!", "danger")

    return redirect(url_for("series", series_id=series_id))

@app.route("/series/<int:series_id>/delete")
@login_required
def delete_series(series_id):
    # check that series exist
    del_series = series_dao.get_series_by_id(series_id)

    if not del_series:
        flash("This series does not exist...", "danger")
        return redirect(url_for("home"))

    if not current_user.id == del_series["creator_id"]:
        flash("Only creator can delete the series...", "danger")
        return redirect(url_for("home"))

    # delete from db
    success = series_dao.del_series(series_id)
    if not success:
        flash("Something went wrong...", "danger")
        return redirect(request.referrer)

    # remove files from filesystem
    os.remove("static/images/" + del_series["title"].lower().replace(" ", "_").replace("'", "_") + ".jpeg")
    shutil.rmtree("static/audio/" + str(del_series["id"]))

    flash("Series deleted correctly!", "success")
    return redirect(url_for("home"))

@app.route("/series/<int:series_id>/add_podcast", methods=["POST"])
@login_required
def new_podcast(series_id):
    # check that series exist
    curr_series = series_dao.get_series_by_id(series_id)
    if not curr_series:
        flash("Cannot add podcast to non existent series...", "danger")
        return redirect(url_for("home"))

    # check user is creator of the series
    if not current_user.id == curr_series["creator_id"]:
        flash("Only creator of the series can add new podcasts...", "danger")
        return redirect(url_for("home"))

    add_podcast = request.form.to_dict()
    audio = request.files["audio"]

    # form validation
    titles = podcast_dao.get_podcasts_titles(series_id)
    for title in titles:
        if title["title"].lower() == add_podcast["title"].lower():
            flash("This title has already been used, please choose a different one...", "warning")
            return redirect(request.referrer)

    if len(add_podcast["title"]) < 3:
        flash("Title is too short...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if add_podcast["title"][0] == " ":
        flash("Title cannot start with an empty space...", "warning")
        return redirect(url_for("series", series_id=series_id))

    special_characters = '"!@#$%^&*()-+?_=,<>/"'
    if any(c in special_characters for c in add_podcast["title"]):
        flash("Title cannot contain special caracters...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if len(add_podcast["title"]) > 30:
        flash("Title is too long...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if len(add_podcast["description"]) < 30:
        flash("Description is too short...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if add_podcast["description"][0] == " ":
        flash("Description cannot start with an empty space...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if len(add_podcast["description"]) > 150:
        flash("Description is too long...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if add_podcast["date"] == "":
        add_podcast["date"] = date.today()

    # handle audio file
    if audio.filename == "":
        flash("Audio is not present...", "warning")
        return redirect(url_for("series", series_id=series_id))

    if "." not in audio.filename or audio.filename.split(".")[1] != "mp3":
        flash("Wrong file extension...", "warning")
        return redirect(url_for("new_podcast"))

    add_podcast["series_id"] = curr_series["id"]
    add_podcast["rnd_seed"] = randint(1,10)

    # save to db
    success = podcast_dao.add_podcast(add_podcast)

    if success:
        audio.save("static/audio/" + str(curr_series["id"]) + "/" + add_podcast["title"].lower().replace(" ", "_").replace("'", "_") + str(add_podcast["rnd_seed"]) + ".mp3")
        flash("Podcast successfully added!", "success")
    else:
        flash("Something went wrong!", "danger")

    return redirect(url_for("series", series_id=curr_series["id"]))

@app.route("/podcast/<int:podcast_id>/edit", methods=["POST"])
@login_required
def edit_podcast(podcast_id):
    # check that podcast exsists
    podcast_edit = podcast_dao.get_podcast(podcast_id)
    if not podcast_edit:
        flash("Podcast do not exist...", "danger")
        return redirect(url_for("home"))

    # get series data
    series_pod = series_dao.get_series_by_id(podcast_edit["series_id"])
    series_id = series_pod["id"]

    # check user is creator of the podcast
    if not current_user.id == series_pod["creator_id"]:
        flash("Only creator of the series can edit podcasts...", "danger")
        return redirect(url_for("series", series_id=series_id))

    add_podcast = request.form.to_dict()
    audio = request.files["audio"]

    # validate form
    if add_podcast["title"] == "":
        add_podcast["title"] = podcast_edit["title"]
    else:
        titles = podcast_dao.get_podcasts_titles(series_id)
        for title in titles:
            if title["title"] == add_podcast["title"]:
                flash("This title has already been used, please choose a different one...", "warning")
                return redirect(request.referrer)

        if len(add_podcast["title"]) < 3:
            flash("Title is too short...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if add_podcast["title"][0] == " ":
            flash("Title cannot start with an empty space...", "warning")
            return redirect(url_for("series", series_id=series_id))

        special_characters = '"!@#$%^&*()-+?_=,<>/"'
        if any(c in special_characters for c in add_podcast["title"]):
            flash("Title cannot contain special caracters...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if len(add_podcast["title"]) > 30:
            flash("Title is too long...", "warning")
            return redirect(url_for("series", series_id=series_id))

    if add_podcast["description"] == "":
        add_podcast["description"] = podcast_edit["description"]
    else:
        if len(add_podcast["description"]) < 30:
            flash("Description is too short...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if add_podcast["description"][0] == " ":
            flash("Description cannot start with an empty space...", "warning")
            return redirect(url_for("series", series_id=series_id))

        if len(add_podcast["description"]) > 150:
            flash("Description is too long...", "warning")
            return redirect(url_for("series", series_id=series_id))

    # handle date
    if add_podcast["date"] == "":
        add_podcast["date"] = podcast_edit["date"]

    # handle audio
    if audio.filename != "":
        if "." not in audio.filename or audio.filename.split(".")[1] != "mp3":
            flash("Wrong file extension...", "warning")
            return redirect(request.referrer)
        if podcast_edit["title"].lower() == add_podcast["title"].lower():
            old_seed = podcast_edit["rnd_seed"]
            new_seed = old_seed
            while new_seed == old_seed:
                new_seed = randint(1,10)
            add_podcast["rnd_seed"] = new_seed
    else:
        add_podcast["rnd_seed"] = podcast_edit["rnd_seed"]

    add_podcast["series_id"] = podcast_edit["series_id"]
    add_podcast["id"] = podcast_edit["id"]

    success = podcast_dao.update_podcast(add_podcast)

    if success:
        if audio.filename == "":
            os.rename("static/audio/" + str(series_pod["id"]) + "/" + podcast_edit["title"].lower().replace(" ", "_").replace("'", "_") + str(podcast_edit["rnd_seed"]) + ".mp3", "static/audio/" + str(series_pod["id"]) + "/" + add_podcast["title"].lower().replace(" ", "_").replace("'", "_") + str(add_podcast["rnd_seed"]) + ".mp3")
        else:
            audio.save("static/audio/" + str(series_pod["id"]) + "/" + add_podcast["title"].lower().replace(" ", "_").replace("'", "_") + str(add_podcast["rnd_seed"]) + ".mp3")
            os.remove("static/audio/" + str(series_pod["id"]) + "/" + podcast_edit["title"].lower().replace(" ", "_").replace("'", "_") + str(podcast_edit["rnd_seed"]) + ".mp3")
        flash("Podcast successfully updated!", "success")
    else:
        flash("Something went wrong!", "danger")

    return redirect(url_for("series", series_id=series_pod["id"]))

@app.route("/podcast/<int:podcast_id>/delete_podcast")
@login_required
def delete_podcast(podcast_id):
    # check that podcast exist
    del_podcast = podcast_dao.get_podcast(podcast_id)
    if not del_podcast:
        flash("This podcast does not exist...", "danger")
        return redirect(url_for("home"))

    # check if user is creator of the series
    podcast_series = series_dao.get_series_by_id(del_podcast["series_id"])

    if not current_user.id == podcast_series["creator_id"]:
        flash("Only creator of the series can delete podcasts...", "danger")
        return redirect(url_for("series", series_id=podcast_series["id"]))

    success = podcast_dao.del_podcast(podcast_id)
    if not success:
        flash("Something went wrong...", "danger")
        return redirect(request.referrer)

    os.remove("static/audio/" + str(podcast_series["id"]) + "/" + del_podcast["title"].lower().replace(" ", "_").replace("'", "_") + str(del_podcast["rnd_seed"]) + ".mp3")

    flash("Podcast deleted correctly!", "success")
    return redirect(url_for("series", series_id=podcast_series["id"]))

@app.route("/profile")
@login_required
def profile():
    follows = follows_dao.get_follows(current_user.id)

    return render_template("profile.html", follows=follows, get_series_by_id=series_dao.get_series_by_id, get_creator=users_dao.get_user_username_by_id)

@app.route("/follow/<int:series_id>")
@login_required
def follow(series_id):
    # check if series exists
    series_follow = series_dao.get_series_by_id(series_id)
    if not series_follow:
        flash("The series you are trying to follow does not exist...", "danger")
        return redirect(url_for("home"))

    # check if user already follows
    already_follows = follows_dao.is_following(current_user.id, series_id)

    if already_follows:
        flash("You already follow this series...", "warning")
        return redirect(request.referrer)

    new_follow = {"user_id": current_user.id, "series_id": series_id}
    success = follows_dao.add_follow(new_follow)
    if not success:
        flash("Something went wrong...", "danger")

    return redirect(request.referrer)

@app.route("/unfollow/<int:series_id>")
@login_required
def unfollow(series_id):
    # check if user is following
    del_follow = follows_dao.is_following(current_user.id, series_id)

    if del_follow:
        success = follows_dao.delete_follow(del_follow["id"])
        if not success:
            flash("Something went wrong...", "danger")
    else:
        flash("You are not following this series...", "danger")
    return redirect(request.referrer)

@app.route("/podcast/<int:podcast_id>/new_comment", methods=["POST"])
@login_required
def new_comment(podcast_id):
    comment = request.form.to_dict()

    # form validation
    if len(comment["comment"]) < 3:
        flash("Comment is too short...", "warning")
        return redirect(request.referrer)

    if comment["comment"][0] == " ":
        flash("Comment cannot start with an empty space...", "warning")
        return redirect(url_for(request.referrer))

    if len(comment["comment"]) > 300:
        flash("Comment is too long...", "warning")
        return redirect(request.referrer)

    # handle date
    comment["date"] = date.today()

    comment["user_id"] = current_user.id
    comment["podcast_id"] = podcast_id

    success = comments_dao.post_comment(comment)

    if not success:
        flash("Something went wrong!", "danger")

    return redirect(request.referrer)

@app.route("/podcast/comments/<int:comment_id>/edit", methods=["POST"])
@login_required
def edit_comment(comment_id):
    comment = request.form.to_dict()
    edit_comment = comments_dao.get_comment(comment_id)

    # check user is comment creator
    if current_user.id != edit_comment["user_id"]:
        flash("Only creator of the comment can edit it...", "danger")
        return redirect(request.referrer)

    # form validation
    if comment["comment"] == "":
        comment["comment"] = edit_comment["comment"]
    else:
        if len(comment["comment"]) < 3:
            flash("Comment is too short...", "warning")
            return redirect(request.referrer)

        if comment["comment"][0] == " ":
            flash("Comment cannot start with an empty space...", "warning")
            return redirect(url_for(request.referrer))

        if len(comment["comment"]) > 300:
            flash("Comment is too long...", "warning")
            return redirect(request.referrer)

    # handle date
    comment["date"] = date.today()

    comment["id"] = comment_id
    comment["podcast_id"] = edit_comment["podcast_id"]

    success = comments_dao.update_comment(comment)

    if not success:
        flash("Something went wrong!", "danger")

    return redirect(request.referrer)

@app.route("/podcast/<int:comment_id>/del_comment")
@login_required
def delete_comment(comment_id):
    # check that comment exist
    comment = comments_dao.get_comment(comment_id)

    if not comment:
        flash("This comment does not exist...", "danger")
        return redirect(url_for("home"))

    # check if user posted the comment
    if not current_user.id == comment["user_id"]:
        flash("Only user who posted the comment can delete it...", "danger")
        return redirect(url_for("home"))

    success = comments_dao.del_comment(comment_id)
    if not success:
        flash("Something went wrong...", "danger")
        return redirect(request.referrer)

    return redirect(request.referrer)

@login_manager.user_loader
def load_user(id):
    user = users_dao.get_user_by_id(id)

    if user is not None:
        user_obj = User(user["id"], user["username"], user["email"], user["password"], user["is_creator"])
    else:
        user_obj = None

    return user_obj

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "GET":
        return render_template("login.html")
    elif request.method == "POST":
        user = request.form.to_dict()

        user_db = users_dao.get_user_by_email(user["email"])

        if not user_db or not check_password_hash(user_db["password"], user["password"]):
            flash("Email or password are wrong, try again...", "warning")
            return redirect(request.referrer)
        else:
            user_obj = User(user_db["id"], user_db["username"], user_db["email"], user_db["password"], user_db["is_creator"])
            success = login_user(user_obj, True)

            if success:
                return redirect(url_for("home"))
            else:
                flash("Something went wrong...", "danger")
                return redirect(request.referrer)

@app.route("/signup", methods=["GET", "POST"])
def signup():
    if request.method == "GET":
        return render_template("signup.html")
    elif request.method == "POST":
        new_user = request.form.to_dict()

        # check that new user is not already signed up (check by email)
        user_db = users_dao.get_user_by_email(new_user["email"])

        if user_db:
            flash("This email has already been used...", "warning")
            return redirect(request.referrer)

        else:
            # check all fields are correct
            if new_user["username"] == "":
                flash("Username is required...", "warning")
                return redirect(request.referrer)

            if len(new_user["username"]) > 20:
                flash("Username is too long...", "warning")
                return redirect(request.referrer)

            if new_user["email"] == "":
                flash("Email is required...", "warning")
                return redirect(request.referrer)

            if len(new_user["password"]) < 8:
                flash("Password is too short...", "warning")
                return redirect(request.referrer)

            if len(new_user["password"]) > 30:
                flash("Password is too long...", "warning")
                return redirect(request.referrer)

            new_user["password"] = generate_password_hash(new_user["password"], method="sha256")
            if new_user.get("is_creator"):
                new_user["is_creator"] = 1
            else:
                new_user["is_creator"] = 0

            success = users_dao.add_user(new_user)

            if success:
                flash("User successfully created!", "success")
                return redirect(url_for("login"))
            else:
                flash("Something went wrong...", "danger")
                return redirect(request.referrer)

@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for("home"))

if __name__ == "__main__":
    app.run("localhost", 3000, True)


N, M = map(int, input().split())
G = [[] for _ in range(N)]
seen = [False for _ in range(N)]

for _ in range(M):
    a, b = map(int, input().split())
    G[a].append(b)
    G[b].append(a)

def dfs(s):
    dist = [-1] * N
    dist[s] = 0
    seen[s]=True
    st = [s]
    while st:
        v = st.pop() #末尾を削除して取り出す
        for nv in G[v]:
            if dist[nv] == -1:
                st.append(nv)
                seen[nv]=True
                dist[nv] = dist[v]+1
    return dist

ans = 0
for i in range(N):
    if seen[i]:
        continue
    dists = dfs(i)
    ans += 1

print(ans)


n, B = list(map(int, input().strip().split()))
T = 0

# your code here
n= int(input("please enter n: "))
B= int(input("please enter B: "))
List = []

for i in range(n):
  i+=1

  if i%2==0:
    a=2**i + 1
    List.append(a)
  else:
    b=3**i + 1
    List.append(b)
f=0
T=1
formula=0
stepNumber=n
while n>0:
  formula+=(List[f]**(n-1)) * T
  n-=1
  f+=1
  if n==0 and formula<B:
    T+=1
    f=0
    formula=0
    n=stepNumber
  if T==10000:
    print("-1")
    break
if T!=10000:
  print(formula)

# please do not modify the input and print statements
# and make sure that your code does not have any other print statements
print(T)


# !/usr/bin/python
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.pylab as pylab
from tensorflow.keras import Sequential, layers, regularizers, models, optimizers
from sklearn.model_selection import train_test_split
import cv2
from sklearn.metrics import confusion_matrix, classification_report

# ********************************* Read in the data ********************************

# define folder paths for the data
train_humans_path = "../data/horse-or-human/train/humans/"
train_horses_path = "../data/horse-or-human/train/horses/"

paths = [train_humans_path, train_horses_path]

X_data = []
y_data = []

for path in paths:
    for file in os.listdir(path):
        image = cv2.imread(os.path.join(path, file), cv2.IMREAD_COLOR)
        X_data.append(image)
        if 'human' in file:
            label = 1
        else:
            label = 0
        y_data.append(label)

X_data = np.asarray(X_data)
y_data = np.asarray(y_data)

# *************************************** Inspect features and labels *****************

print("\nShape of X_data:", X_data.shape)
print("Shape of y_data:", y_data.shape)

print("\nData type of X_data:", type(X_data))
print("Data type of y_data:", type(y_data))

print("\nData type of 1st sample in X_data:", type(X_data[0]))
print("Data type of 1st label in y_data:", type(y_data[0]))

print("\nThe first X_data sample: \n", X_data[0])

print("\nThe y_data labels: \n", y_data)
print(f"\nNumber of y_data labels that are ones (expected 527): {y_data.sum()}")

# ****************************** Scale features ****************************************

X_data = X_data / 255.0
print("\nThe first X_data sample after normalization: \n", X_data[0])

# ****************************** Split into train and test sets *************************

X_train, X_valid, y_train, y_valid = train_test_split(X_data, y_data, test_size=0.2, random_state=42)

print(f"\nShape of X_train: {X_train.shape}")
print(f"Shape of X_valid: {X_valid.shape}")
print(f"Shape of y_train: {y_train.shape}")
print(f"Shape of y_valid: {y_valid.shape}\n")

# ****************************** Build model and fit ***********************************

# Build model
clf = Sequential()
clf.add(layers.Conv2D(16, (3, 3), strides=(1, 1), activation='relu', input_shape=(300, 300, 3)))
clf.add(layers.MaxPooling2D(2, 2))
clf.add(layers.Conv2D(32, (3, 3), strides=(1, 1), activation='relu'))
clf.add(layers.MaxPooling2D(2, 2))
clf.add(layers.Conv2D(64, (3, 3), strides=(1, 1), activation='relu'))
clf.add(layers.MaxPooling2D(2, 2))
clf.add(layers.Conv2D(64, (3, 3), strides=(1, 1), activation='relu'))
clf.add(layers.MaxPooling2D(2, 2))
clf.add(layers.Flatten())
clf.add(layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l2(0.03)))
# clf.add(layers.Dense(16, activation='relu'))
clf.add(layers.Dropout(0.5))
clf.add(layers.Dense(1, activation='sigmoid'))

# Print summary of model
clf.summary()

# Compile model and fit
opt = optimizers.Adam(lr=0.001)
clf.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])
history = clf.fit(X_train, y_train, validation_split=0.2, epochs=25, verbose=2)

# ********************************** Save Model ***************************************

clf.save('model')

# ************************** Evaluate metrics on training data ************************

_, accuracy = clf.evaluate(X_train, y_train)
print('\nAccuracy on training data is: {:.1%}'.format(accuracy))

# ******************************* Visualize results ***********************************

params = {'legend.fontsize': 'x-large',
          'axes.labelsize': 'x-large',
          'axes.titlesize': 'x-large',
          'xtick.labelsize': 'x-large',
          'ytick.labelsize': 'x-large'}
pylab.rcParams.update(params)

fig, axes = plt.subplots(2, figsize=(15, 15), sharex=True)
axes[0].plot(history.history['accuracy'])
axes[0].plot(history.history['val_accuracy'])
axes[0].set_title('Model Accuracy')
axes[0].set_ylabel('Accuracy')
axes[0].set_ylim(ymax=1.0)
axes[0].grid()
axes[0].legend(['Train', 'Validation'], loc='lower right')
axes[1].plot(history.history['loss'])
axes[1].plot(history.history['val_loss'])
axes[1].set_title('Model Loss')
axes[1].set_ylabel('Loss')
# axes[1].set_yscale("log")
axes[1].set_xlabel('Epoch')
axes[1].grid()
axes[1].legend(['Train', 'Validation'], loc='upper right')
plt.savefig('../results/fig_acc_loss.svg', dpi=400, bbox_inches='tight')

# ******************************* Load Model *************************************************

reconstructed_model = models.load_model('model')  # comment out if not loading saved model

# ******************************* Evaluate metrics on validation data *******************************************

_, accuracy = reconstructed_model.evaluate(X_valid, y_valid)  # only use when loading saved model
# _, accuracy = clf.evaluate(X_test, y_test)  # only use when using model in memory
print('\nAccuracy on validation hold-out set is: {:.1%}\n'.format(accuracy))

y_pred_valid = clf.predict_classes(X_valid)

# Confusion matrix and classification report
cm = confusion_matrix(y_valid, y_pred_valid)
print("Confusion matrix:\n", cm)

cr = classification_report(y_valid, y_pred_valid)
print("\nClassification report:\n", cr)


import io
from telebot import types
from cs_bot.util.user_action import on_user_action
from cs_bot.roadmap import menu
from cs_bot.roadmap.util import build_roadmap

def _get_roadmap(db, chat_id):
    course_prerequisites = db.get_all_courses_prerequisites(use_cache=True)
    completed_courses = db.get_completed_courses(chat_id)
    available_courses = set()
    for course in db.get_course_list(use_cache=True):
        if course_prerequisites[course].issubset(completed_courses):
            available_courses.add(course)
    return build_roadmap(available_courses, completed_courses)

@on_user_action(db_ind=1, data_ind=2, data_type=types.Message, add_user=True)
def send_roadmap_menu(bot, db, message):
    roadmap = _get_roadmap(db, message.chat.id)
    bot.send_photo(message.chat.id, roadmap, reply_markup=menu.roadmap_menu)

@on_user_action(db_ind=1, data_ind=2, data_type=types.CallbackQuery)
def send_orig_roadmap_menu(bot, db, call):
    roadmap = _get_roadmap(db, call.message.chat.id)
    roadmap_bytes = io.BytesIO()
    roadmap.save(roadmap_bytes, format='png')
    bot.send_document(call.message.chat.id, roadmap_bytes.getvalue(), visible_file_name='Roadmap.png')


# This is a python script for coding angle dependent acoustic backscatter
# This is to test if git commit is working
# Alwife first push
# 1
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
from scipy.special import gamma, jv
# import scipy as sp
import math
# import cmath

# Define main constants:
c_w = 1485  # m/s speed of sound in water
f_a = 30  # kHz
k_a = 2 * np.pi*f_a*(10**3)/c_w/100  # Wave-number in water in cm^-1
c_zeta = .1
c_zeta_2 = c_zeta ** 2
r_normal = 1

lowerbound = 0
upperbound = 10**3
step = 10**-1
integrand_vector = np.arange(lowerbound, upperbound, step)

# Define functions:
def func_q(theta, alpha):  # This throws some form of error when a 0 is input, it returns a nan
    first = np.cos(theta)**2
    second = np.sin(theta)**(-2*alpha)
    third = c_zeta_2
    fourth = 2**(1-2*alpha)
    fifth = k_a**(2*(1-alpha))
    return first*second*third*fourth*fifth

def si_integrand(x, q, alpha):
    return np.exp(-q*x**(2*alpha))*jv(0, x)*x

def func_si(theta, q, alpha, lower_bound, upper_bound, step_size):
    if theta > 0:
        # Define integration by series bounds, samples, and dx
        du = step_size

        # Calculate si
        first = r_normal ** 2
        second = (8*np.pi*np.cos(theta)**2 * np.sin(theta)**2)**-1

        # # Using standard integration
        # third = integrate.quad(lambda x: np.exp(-q * x ** (2 * alpha)) * np.i0(x) * x, lower_bound, upper_bound)
        # return first*second*third[0]

        # Using integral of a bessel function
        third = integrate.quad(lambda u: np.exp(-q * u ** (2 * alpha)) * jv(0, u) * u, lower_bound, upper_bound)
        return first * second * third[0]

        # # Using summation
        # sum = 0
        # u = lower_bound + step_size
        # while u < upper_bound:
        #     one = jv(0, u)
        #     two = np.exp(-q*u**(2*alpha))*u
        #     sum += du*one*two
        #     u += step_size
        #
        # return first*second*sum

    elif theta == 0:
        first = r_normal ** 2
        second = (8 * np.pi * alpha) ** -1
        third = c_zeta ** (-2 / alpha)
        fourth = (2 * k_a ** 2) ** ((alpha - 1) / alpha)
        fifth = gamma(1 / alpha)
        return first * second * third * fourth * fifth

    else:
        raise RuntimeError("Theta is less than zero; cannot compute.")

# Define main() function:
def main():
    print(k_a)
    # Define alpha and theta values
    alpha_array = np.array([1, 0.85, 0.75, 0.6, 0.5])

    theta_array_grazing = np.arange(70, 89, .5)
    theta_array_incidence = 90 - theta_array_grazing
    theta_array = theta_array_incidence*(np.pi/180)

    # Allocate memory
    data_points = len(theta_array)
    data_rows = len(alpha_array)
    empty_data_array = np.zeros([data_rows, data_points])
    alpha_q_array = np.copy(empty_data_array)
    si_array = np.copy(empty_data_array)

    # # Iterate through alpha and theta to populate alpha, q data array
    # alpha_index = 0
    # for alpha in alpha_array:
    #     q_index = 0
    #     for theta in theta_array:
    #         alpha_q_array[alpha_index, q_index] = func_q(theta, alpha)
    #         q_index += 1
    #     alpha_index += 1

    #Iterate through alpha and q to populate alpha, Si array
    # Calculate si values
    for alpha_index, alpha in enumerate(alpha_array):
        for theta_index, theta in enumerate(theta_array):
            si_array[alpha_index, theta_index] = \
                func_si(theta, func_q(theta, alpha), alpha, lowerbound, upperbound, step)

    # Convert Si values to dB
    si_array_dB = 10*np.log10(si_array)

    # Plot si values:
    x = theta_array_grazing
    y = 0
    # plt.plot(x, si_array_dB[y, :], label=f'Alpha = {alpha_array[y]}')
    while y < len(alpha_array):
        plt.plot(x, si_array_dB[y, :], label=f'{alpha_array[y]}')
        y += 1
    plt.title('Si Value by Alpha Value')
    plt.xlabel('Grazing Angle (deg)')
    plt.ylabel('Si Value')
    plt.legend()
    plt.grid()
    plt.gcf().autofmt_xdate()  # beautify the x-labels
    plt.show()

    # # Plot integrand sum values:
    # plt.plot(integrand_vector, func_si(theta_array[0], .023, 1, lowerbound, upperbound, step))
    # # while y < len(alpha_array):
    # #     plt.plot(x, alpha_si_array[y, :], label=f'{alpha_array[y]}')
    # #     y += 1
    # plt.title('Integrand sum alpha = 1, q = 3')
    # plt.xlabel('u')
    # plt.ylabel('Cumulative sum for du Value')
    # plt.grid()
    # plt.gcf().autofmt_xdate()  # beautify the x-labels
    # plt.show()

# Run main() function:
main()


#code:utf8
import gensim
import numpy as np
import csv,os,sys
import tensorflow as tf
import pickle
import json
import multiprocessing
import datetime
import sklearn.metrics
import matplotlib.pyplot as plt

APP_DIR  = os.path.dirname(os.path.realpath(__file__))

def trans_gensim_word2vec2tf_embedding(word2vector_file_path:str):
    """把gensim的word2vec结果转化为tf.keras.layers.Embedding需要的结果
    """

    word2vec_model = gensim.models.Word2Vec.load(word2vector_file_path)

    #所有的词
    word_list = [word for word, word_info in word2vec_model.wv.vocab.items()]

    #词到index的映射
    word2index_dict = {"<PADDING>": 0, "<UNK>":1}

    #保存特殊词的padding
    specical_word_count = len(word2index_dict)

    #词到词向量的映射
    word2vector_dict = {}

    #初始化embeddings_matrix

    embeddings_matrix = np.zeros((len(word_list) + specical_word_count, word2vec_model.vector_size))
    #初始化unk为-1,1分布
    embeddings_matrix[word2index_dict["<UNK>"]] = (1 / np.sqrt(len(word_list) + specical_word_count) * (2 * np.random.rand(word2vec_model.vector_size) - 1))

    for i,word in enumerate(word_list):
        #从0开始
        word_index = i + specical_word_count
        word2index_dict[str(word)] = word_index
        word2vector_dict[str(word)] = word2vec_model.wv[word] # 词语：词向量
        embeddings_matrix[word_index] = word2vec_model.wv[word]  # 词向量矩阵

    #写入文件
    with open(os.path.join(APP_DIR,"data/word2index.bin"),"wb") as f:
        pickle.dump(word2index_dict,f)

    return embeddings_matrix,word2vector_dict,word2index_dict

def trans2index(word2index_dict,word):
    """转换"""
    if word in word2index_dict:
        return word2index_dict[word]
    else:
        if "<UNK>" in word2index_dict:
            return word2index_dict["<UNK>"]
        else:
            raise ValueError("没有这个值，请检查")

def trans_data2tf_data(data_file_path:str,word2index_dict=None):
    """把data文件转化为tf.data
    """
    tag2index_dict = {"<PADDING>":0}
    tag_index_count = len(tag2index_dict)
    x_list = []
    y_list = []
    #读取数据，并转化为index
    with open(data_file_path,encoding="utf-8") as f:
        if word2index_dict is None:
            #todo
            pass
        else:
            temp_x_list = []
            temp_y_list = []
            for i,row in enumerate(f):
                if i % 10**7 == 0:
                    tf.print("已经读取{}行".format(i))
                row = row.strip()
                if row:
                    x,y = row.split(" ")
                    if not y in tag2index_dict:
                        tag2index_dict[y] = tag_index_count
                        tag_index_count += 1
                    temp_x_list.append(trans2index(word2index_dict,x))
                    temp_y_list.append(tag2index_dict[y])
                else:
                    if temp_x_list and temp_y_list:
                        x_list.append(temp_x_list)
                        y_list.append(temp_y_list)
                    else:
                        tf.print(temp_x_list,temp_x_list)
                    temp_x_list = []
                    temp_y_list = []
    tf.print("x_list[:3]:{}".format(x_list[:3]))
    tf.print("y_list[:3]:{}".format(y_list[:3]))

    #写入文件
    with open(os.path.join(APP_DIR,"data/tag2index.bin"),"wb") as f:
        pickle.dump(tag2index_dict,f)

    #这里需要省略比较长的句子,取正态分布的99.7
    x_max_length0 = np.max(np.array([len(v) for v in x_list]))
    x_max_length = int(np.max(np.percentile(np.array([len(v) for v in x_list]),99.7)))
    y_max_length = int(np.max(np.percentile(np.array([len(v) for v in y_list]),99.7)))
    tf.print("数据集中最长的句子长度为:{},设定的最长的句子长度为:{}".format(x_max_length0,x_max_length))

    x_npa = np.array(x_list)
    del x_list
    y_npa = np.array(y_list)
    del y_list

    x_npa = tf.keras.preprocessing.sequence.pad_sequences(x_npa,maxlen=x_max_length,dtype=np.int32,truncating="post", padding='post',value=0)
    y_npa = tf.keras.preprocessing.sequence.pad_sequences(y_npa,maxlen=y_max_length,dtype=np.int32,truncating="post", padding='post',value=0)

    tf.print("x_npa[-3:]:{}".format(x_npa[-3:]))
    tf.print("y_npa[-3:]:{}".format(y_npa[-3:]))

    return tf.data.Dataset.from_tensor_slices((x_npa,y_npa)),tag2index_dict


from argparse import ArgumentParser
from recsys.utils import parse_config
from recsys.core.handler import RequestHandler

CONFIG_FILE = 'config.yaml'

def parse_arguments():
    parser = ArgumentParser()
    parser.add_argument(
        '-m', '--movies', nargs='+',
        help=('Favorite movies you want to'
              ' get recommendations based on.')
    )
    args = parser.parse_args()
    return args

def main():
    arguments = parse_arguments()
    config = parse_config(CONFIG_FILE)
    handler = RequestHandler(config['inference'], config['logger'])
    print(handler.handle_request(arguments.movies))

if __name__ == '__main__':
    main()


"""
尝试预处理
"""
import SimpleITK as sitk
import os
# from skimage.transform import resize
import cv2
import numpy
from skimage.morphology import skeletonize_3d
import numpy as np
import nibabel as nib
import sys
from matplotlib import pylab as plt
from nibabel.viewers import OrthoSlicer3D

vol_path = os.path.join(os.getcwd(), 'data/imagesTr')
seg_path = os.path.join(os.getcwd(), 'data/labelsTr')
vol_list = os.listdir(vol_path)
seg_list = os.listdir(seg_path)

ct_file = os.path.join(vol_path, vol_list[0])
seg_file = os.path.join(seg_path, seg_list[0])
ct_img = sitk.ReadImage(ct_file)

seg_img = sitk.Cast(sitk.ReadImage(seg_file), sitk.sitkInt16)
seg_array = sitk.GetArrayFromImage(seg_img)

ct_img.SetOrigin(seg_img.GetOrigin())
ct_img.SetSpacing(seg_img.GetSpacing())
ct_array = sitk.GetArrayFromImage(ct_img)
# ct_img = nib.load(ct_file)
# seg_img=nib.load(seg_file)
# seg_img=sitk.ReadImage(seg_file)
print(seg_array.dtype)

z = np.any(seg_array, axis=(1, 2))
print(z.shape)
startposition1, endposition1 = np.where(z)[0][[0, -1]]
# print(startposition,endposition)
# ct_array=ct_array[startposition-2:endposition+2]
print(ct_array.shape)

z = np.any(seg_array, axis=(0, 2))
print(z.shape)
startposition2, endposition2 = np.where(z)[0][[0, -1]]
# print(startposition,endposition)
# ct_array=ct_array[:][startposition-2:endposition+2]

z = np.any(seg_array, axis=(0, 1))
print(z.shape)
startposition3, endposition3 = np.where(z)[0][[0, -1]]
# print(startposition3,endposition3)
# ct_array=ct_array[:][:][startposition-2:endposition+2]
# print(ct_array.shape)
ct_array = ct_array[startposition1 - 2:endposition1 + 2, startposition2 - 2:endposition2 + 2,
           startposition3 - 2:endposition3 + 2]
seg_array = seg_array[startposition1 - 2:endposition1 + 2, startposition2 - 2:endposition2 + 2,
            startposition3 - 2:endposition3 + 2]
print(ct_array.shape)
# resized_ct = numpy.zeros(256 * 256 * 256).reshape(256, 256, 256)
# resized_seg = numpy.zeros(256 * 256 * 256).reshape(256, 256, 256)
# for i in range(ct_array.shape[0]):
#     resized_ct[i] = cv2.resize(ct_array[i], (256, 256), interpolation=cv2.INTER_NEAREST)
#     resized_seg[i] = cv2.resize(seg_array[i], (256, 256), interpolation=cv2.INTER_NEAREST)
# for i in range(ct_array.shape[1]):
#     resized_ct[:, i, :] = cv2.resize(ct_array[:, i, :], (256, 256),interpolation=cv2.INTER_LINEAR)
#     resized_seg[:, i, :] = cv2.resize(seg_array[:, i, :], (256, 256),interpolation=cv2.INTER_NEAREST)
# for i in range(ct_array.shape[2]):
#     resized_ct[:, :, i] = cv2.resize(ct_array[:, :, i], (256, 256),interpolation=cv2.INTER_NEAREST)
#     resized_seg[:, :, i] = cv2.resize(seg_array[:, :, i], (256, 256),interpolation=cv2.INTER_NEAREST)

# print(resized_ct.shape)
# ct_array=np.resize(ct_array,[256,256,256])
# for key in seg_img.GetMetaDataKeys():
#     print(key,seg_img.GetMetaData(key))
# print('='*30)
# for key in ct_img.GetMetaDataKeys():
#     print(key,ct_img.GetMetaData(key))

# labelstats = sitk.LabelOverlapMeasuresImageFilter()
# labelstats.Execute(ct_img,seg_img)
ct_out = sitk.GetImageFromArray(ct_array)
seg_out = sitk.GetImageFromArray(seg_array)
# ct_out = sitk.GetImageFromArray(resized_ct)
# seg_out = sitk.GetImageFromArray(resized_seg)
sitk.WriteImage(ct_out, "volume.nii")
sitk.WriteImage(seg_out, "segmentation.nii")


words = []
status = "on"

print("List words in alphabetical order. Leave input empty (write nothing and press ENTER) to end proecss.")

print("""\nWhat should be prioritised?
\t[A] Lowercase letters
\t[B] Uppercase letters
""")

choice = input("Choice (A or B): ")
print("\n")

while status == "on":
	word = input("Word: ")
	if word == "":
		status = "off"
	else:
		words.append(word)

if choice == "A":
	words.sort(key=str.lower)
else:
	words.sort(key=str.upper)

print("==========================")

for i in words:
	print(i)