"""
Nurbs main operations algorithms.
"""

from functools import lru_cache
import numpy as np

import volmdlr
from volmdlr.nurbs import core

def knot_insertion(degree, knotvector, ctrlpts, u, **kwargs):
    """
    Computes the control points of the rational/non-rational spline after knot insertion.

    Part of Algorithm A5.1 of The NURBS Book by Piegl & Tiller, 2nd Edition.

    Keyword Arguments:
        * ``num``: number of knot insertions. *Default: 1*
        * ``s``: multiplicity of the knot. *Default: computed via :func:`.find_multiplicity`*
        * ``span``: knot span. *Default: computed via :func:`.find_span_linear`*

    :param degree: degree
    :type degree: int
    :param knotvector: knot vector
    :type knotvector: list, tuple
    :param ctrlpts: control points
    :type ctrlpts: list
    :param u: knot to be inserted
    :type u: float
    :return: updated control points
    :rtype: list

    """
    # Get keyword arguments
    num_insertions = kwargs.get('num', 1)  # number of knot insertions
    knot_multiplicity = kwargs.get('s', core.find_multiplicity(u, knotvector))  # multiplicity
    knot_span = kwargs.get('span', core.find_span_linear(degree, knotvector, len(ctrlpts), u))  # knot span

    # Initialize variables
    num_ctrlpts_new = len(ctrlpts) + num_insertions

    # Initialize new control points array (control points may be weighted or not)
    ctrlpts_new = [np.empty_like(ctrlpts[0]) for _ in range(num_ctrlpts_new)]

    # Initialize a local array of length p + 1
    temp = [np.empty_like(ctrlpts[0]) for _ in range(degree + 1)]

    # Save unaltered control points
    ctrlpts_new[:knot_span - degree + 1] = ctrlpts[:knot_span - degree + 1]
    ctrlpts_new[knot_span - knot_multiplicity + num_insertions:] = ctrlpts[knot_span - knot_multiplicity:]

    # Start filling the temporary local array which will be used to update control points during knot insertion
    for i in range(degree - knot_multiplicity + 1):
        temp[i] = np.array(ctrlpts[knot_span - degree + i])

    # Insert knot "num_insertions" times
    for j in range(1, num_insertions + 1):
        new_knot_index = knot_span - degree + j
        for i in range(degree - j - knot_multiplicity + 1):
            alpha = knot_insertion_alpha(u, tuple(knotvector), knot_span, i, new_knot_index)
            temp[i] = alpha * temp[i + 1] + (1.0 - alpha) * temp[i]
        ctrlpts_new[new_knot_index] = temp[0]
        ctrlpts_new[knot_span + num_insertions - j - knot_multiplicity] = \
            temp[degree - j - knot_multiplicity]

    # Load remaining control points
    new_knot_index = knot_span - degree + num_insertions
    ctrlpts_new[new_knot_index + 1:knot_span - knot_multiplicity] = \
        temp[1:knot_span - knot_multiplicity - new_knot_index + 1]

    # Return control points after knot insertion
    return ctrlpts_new

@lru_cache(maxsize=128)
def knot_insertion_alpha(u, knotvector, span, idx, leg):
    """
    Computes :math:`\\alpha` coefficient for knot insertion algorithm.

    :param u: knot
    :type u: float
    :param knotvector: knot vector
    :type knotvector: tuple
    :param span: knot span
    :type span: int
    :param idx: index value (degree-dependent)
    :type idx: int
    :param leg: i-th leg of the control points polygon
    :type leg: int
    :return: coefficient value
    :rtype: float
    """
    return (u - knotvector[leg + idx]) / (knotvector[idx + span + 1] - knotvector[leg + idx])

def knot_insertion_kv(knotvector, u, span, num_insertions):
    """
    Computes the knot vector of the rational/non-rational spline after knot insertion.

    Part of Algorithm A5.1 of The NURBS Book by Piegl & Tiller, 2nd Edition.

    :param knotvector: knot vector
    :type knotvector: list, tuple
    :param u: knot
    :type u: float
    :param span: knot span
    :type span: int
    :param num_insertions: number of knot insertions
    :type num_insertions: int
    :return: updated knot vector
    :rtype: list
    """
    kv_size = len(knotvector)
    kv_updated = [0.0 for _ in range(kv_size + num_insertions)]

    # Compute new knot vector
    for i in range(0, span + 1):
        kv_updated[i] = knotvector[i]
    for i in range(1, num_insertions + 1):
        kv_updated[span + i] = u
    for i in range(span + 1, kv_size):
        kv_updated[i + num_insertions] = knotvector[i]

    # Return the new knot vector
    return kv_updated

def insert_knot_curve(obj, param, num, **kwargs):
    """
    Inserts knots n-times to a spline geometry.

    Keyword Arguments:
        * ``check_num``: enables/disables operation validity checks. *Default: True*

    :param obj: spline geometry
    :param param: knot(s) to be inserted in [u, v, w] format
    :type param: list, tuple
    :param num: number of knot insertions in [num_u, num_v, num_w] format
    :type num: list, tuple
    :return: updated spline geometry

    """
    # Get keyword arguments
    check_num = kwargs.get('check_num', True)  # can be set to False when the caller checks number of insertions

    if check_num:
        # Check the validity of number of insertions
        if not isinstance(num, (list, tuple)):
            raise ValueError("The number of insertions must be a list or a tuple")

        if len(num) != 1:
            raise ValueError("The length of the num array must be equal to the number of parametric dimensions")

        for val in num:
            if val < 0:
                raise ValueError('Number of insertions must be a positive integer value')

    if param[0] is not None and num[0] > 0:
        # Find knot multiplicity
        param_multiplicity = core.find_multiplicity(param[0], obj.knotvector)

        # Check if it is possible add that many number of knots
        if check_num and num[0] > obj.degree - param_multiplicity:
            raise ValueError("Knot " + str(param[0]) + " cannot be inserted " + str(num[0]) + " times")

        # Find knot span
        span = core.find_span_linear(obj.degree, obj.knotvector, len(obj.ctrlpts), param[0])

        # Compute new knot vector
        kv_new = knot_insertion_kv(obj.knotvector, param[0], span, num[0])

        # Compute new control points
        cpts = obj.ctrlptsw if obj.rational else obj.ctrlpts
        cpts_tmp = knot_insertion(obj.degree, obj.knotvector, cpts, param[0],
                                  num=num[0], s=param_multiplicity, span=span)
        weights = None
        if obj.rational:
            cpts_tmp, weights = separate_ctrlpts_weights(cpts_tmp)

        # Create new curve
        knots, knot_multiplicities = get_knots_and_multiplicities(kv_new)
        point_name = "Point" + obj.__class__.__name__[-2:]
        cpts_tmp = [getattr(volmdlr, point_name)(*point) for point in cpts_tmp]
    # Return new spline geometry
    return obj.__class__(obj.degree, cpts_tmp, knot_multiplicities, knots, weights)

def split_curve(obj, param, **kwargs):
    """
    Splits the curve at the input parametric coordinate.

    This method splits the curve into two pieces at the given parametric coordinate, generates two different
    curve objects and returns them. It does not modify the input curve.

    Keyword Arguments:
        * ``find_span_func``: FindSpan implementation. *Default:* :func:`.helpers.find_span_linear`
        * ``insert_knot_func``: knot insertion algorithm implementation. *Default:* :func:`.operations.insert_knot`

    :param obj: Curve to be split
    :type obj: abstract.Curve
    :param param: parameter
    :type param: float
    :return: a list of curve segments
    :rtype: list

    """
    if param in set(obj.domain):
        raise ValueError("Cannot split from the domain edge")

    # Find multiplicity of the knot and define how many times we need to add the knot
    knot_multiplicity = core.find_multiplicity(param, obj.knotvector)
    insertion_count = obj.degree - knot_multiplicity
    knot_span = core.find_span_linear(obj.degree, obj.knotvector, len(obj.ctrlpts), param) - obj.degree + 1

    # Insert knot
    temp_obj = insert_knot_curve(obj, [param], num=[insertion_count], check_num=False)

    # Knot vectors
    curve1_kv, curve2_kv = helper_split_knot_vectors(temp_obj.degree, temp_obj.knotvector, len(temp_obj.ctrlpts),
                                                     param,
                                                     core.find_span_linear)

    return construct_split_curve(temp_obj, curve1_kv, curve2_kv, knot_span, insertion_count)

def construct_split_curve(obj, curve1_kv, curve2_kv, knot_span, insertion_count):
    """
    Helper function to instantiate split curve.
    """
    control_points = obj.control_points
    curve1_ctrlpts = control_points[0:knot_span + insertion_count]
    curve2_ctrlpts = control_points[knot_span + insertion_count - 1:]
    if obj.rational:
        curve1_weights = obj.weights[0:knot_span + insertion_count]
        curve2_weights = obj.weights[knot_span + insertion_count - 1:]
    else:
        curve1_weights = None
        curve2_weights = None

    knots_1, knot_multiplicities_1 = get_knots_and_multiplicities(curve1_kv)

    knots_2, knot_multiplicities_2 = get_knots_and_multiplicities(curve2_kv)

    # Return the split curves
    return [obj.__class__(obj.degree, curve1_ctrlpts, knot_multiplicities_1, knots_1, curve1_weights),
            obj.__class__(obj.degree, curve2_ctrlpts, knot_multiplicities_2, knots_2, curve2_weights)]

def insert_knot_surface(obj, param, num, **kwargs):
    """
    Inserts knots n-times to a spline geometry.

    Keyword Arguments:
        * ``check_num``: enables/disables operation validity checks. *Default: True*

    :param obj: spline geometry
    :param param: knot(s) to be inserted in [u, v, w] format
    :type param: list, tuple
    :param num: number of knot insertions in [num_u, num_v, num_w] format
    :type num: list, tuple
    :return: updated spline geometry

    """
    # Get keyword arguments
    check_num = kwargs.get('check_num', True)  # can be set to False when the caller checks number of insertions
    # u-direction
    if param[0] is not None and num[0] > 0:
        # Find knot multiplicity
        knotvector = obj.knots_vector_u
        param_multiplicity = core.find_multiplicity(param[0], knotvector)

        # Check if it is possible add that many number of knots
        if check_num and num[0] > obj.degree_u - param_multiplicity:
            raise ValueError("Knot " + str(param[0]) + " cannot be inserted " + str(num[0]) + " times (u-dir)")

        # Find knot span
        span = core.find_span_linear(obj.degree_u, knotvector, obj.nb_u, param[0])

        # Compute new knot vector
        new_kv = knot_insertion_kv(knotvector, param[0], span, num[0])

        # Get curves
        cpts_tmp = []
        cpts = obj.ctrlptsw if obj.rational else obj.ctrlpts
        for v in range(obj.nb_v):
            ctrlpts = [cpts[v + (obj.nb_v * u)] for u in range(obj.nb_u)]
            ctrlpts_tmp = knot_insertion(obj.degree_u, knotvector, ctrlpts, param[0],
                                         num=num[0], s=param_multiplicity, span=span)
            cpts_tmp += ctrlpts_tmp

        # Update the surface after knot insertion
        knots, multiplicities = get_knots_and_multiplicities(new_kv)
        cpts_tmp = flip_ctrlpts_u(cpts_tmp, obj.nb_u + num[0], obj.nb_v)
        weights = None
        if obj.rational:
            cpts_tmp, weights = separate_ctrlpts_weights(cpts_tmp)
        control_points = [volmdlr.Point3D(*point) for point in cpts_tmp]
        obj = obj.__class__(obj.degree_u, obj.degree_v,
                            control_points,
                            obj.nb_u + num[0], obj.nb_v, multiplicities, obj.v_multiplicities,
                            knots, obj.v_knots, weights)
    # v-direction
    if param[1] is not None and num[1] > 0:
        # Find knot multiplicity
        knotvector = obj.knots_vector_v
        param_multiplicity = core.find_multiplicity(param[1], knotvector)

        # Check if it is possible add that many number of knots
        if check_num and num[1] > obj.degree_v - param_multiplicity:
            raise ValueError("Knot " + str(param[1]) + " cannot be inserted " + str(num[1]) + " times (v-dir)")

        # Find knot span
        span = core.find_span_linear(obj.degree_v, knotvector, obj.nb_v, param[1])

        # Compute new knot vector
        new_kv = knot_insertion_kv(knotvector, param[1], span, num[1])

        # Get curves
        cpts_tmp = []
        cpts = obj.ctrlptsw if obj.rational else obj.ctrlpts
        for u in range(obj.nb_u):
            ctrlpts = [cpts[v + (obj.nb_v * u)] for v in range(obj.nb_v)]
            ctrlpts_tmp = knot_insertion(obj.degree_v, knotvector, ctrlpts, param[1],
                                         num=num[1], s=param_multiplicity, span=span)
            cpts_tmp += ctrlpts_tmp

        knots, multiplicities = get_knots_and_multiplicities(new_kv)
        weights = None
        if obj.rational:
            cpts_tmp, weights = separate_ctrlpts_weights(cpts_tmp)
        control_points = [volmdlr.Point3D(*point) for point in cpts_tmp]
        # Update the surface after knot insertion
        obj = obj.__class__(obj.degree_u, obj.degree_v,
                            control_points, obj.nb_u, obj.nb_v + num[1], obj.u_multiplicities, multiplicities,
                            obj.u_knots, knots, weights)
    return obj

def split_surface_u(obj, param, **kwargs):
    """
    Splits the surface at the input parametric coordinate on the u-direction.

    This method splits the surface into two pieces at the given parametric coordinate on the u-direction,
    generates two different surface objects and returns them. It does not modify the input surface.

    Keyword Arguments:
        * ``find_span_func``: FindSpan implementation. *Default:* :func:`.helpers.find_span_linear`
        * ``insert_knot_func``: knot insertion algorithm implementation. *Default:* :func:`.operations.insert_knot`

    :param obj: surface
    :type obj: abstract.Surface
    :param param: parameter for the u-direction
    :type param: float
    :return: a list of surface patches
    :rtype: list

    """

    if param in (obj.domain[0], obj.domain[1]):
        raise ValueError("Cannot split from the u-domain edge")

    # Keyword arguments
    span_func = kwargs.get('find_span_func', core.find_span_linear)  # FindSpan implementation
    insert_knot_func = kwargs.get('insert_knot_func', insert_knot_surface)  # Knot insertion algorithm

    # Find multiplicity of the knot
    knotvector_u = obj.knots_vector_u
    knot_span = span_func(obj.degree_u, knotvector_u, obj.nb_u, param) - obj.degree_u + 1
    knot_multiplicity = core.find_multiplicity(param, knotvector_u)
    insertion_count = obj.degree_u - knot_multiplicity

    # Split the original surface
    temp_obj = insert_knot_func(obj, [param, None], num=[insertion_count, 0], check_num=False)

    # Knot vectors
    knotvectors = helper_split_knot_vectors(temp_obj.degree_u, temp_obj.knots_vector_u,
                                            temp_obj.nb_u, param, span_func)

    return construct_split_surfaces(temp_obj, knotvectors, "u", knot_span, insertion_count)

def split_surface_v(obj, param, **kwargs):
    """
    Splits the surface at the input parametric coordinate on the v-direction.

    This method splits the surface into two pieces at the given parametric coordinate on the v-direction,
    generates two different surface objects and returns them. It does not modify the input surface.

    Keyword Arguments:
        * ``find_span_func``: FindSpan implementation. *Default:* :func:`.helpers.find_span_linear`
        * ``insert_knot_func``: knot insertion algorithm implementation. *Default:* :func:`.operations.insert_knot`

    :param obj: surface
    :type obj: abstract.Surface
    :param param: parameter for the v-direction
    :type param: float
    :return: a list of surface patches
    :rtype: list

    """
    if param in (obj.domain[2], obj.domain[3]):
        raise ValueError("Cannot split from the v-domain edge")

    # Keyword arguments
    span_func = kwargs.get('find_span_func', core.find_span_linear)  # FindSpan implementation
    insert_knot_func = kwargs.get('insert_knot_func', insert_knot_surface)  # Knot insertion algorithm

    # Find multiplicity of the knot
    knotvector_v = obj.knots_vector_v
    knot_span = span_func(obj.degree_v, knotvector_v, obj.nb_v, param) - obj.degree_v + 1
    knot_multiplicity = core.find_multiplicity(param, knotvector_v)
    insertion_count = obj.degree_v - knot_multiplicity

    # Split the original surface
    temp_obj = insert_knot_func(obj, [None, param], num=[0, insertion_count], check_num=False)

    # Knot vectors
    knotvectors = helper_split_knot_vectors(temp_obj.degree_v, temp_obj.knots_vector_v, temp_obj.nb_v, param,
                                                   span_func)
    return construct_split_surfaces(temp_obj, knotvectors, "v", knot_span, insertion_count)

def separate_ctrlpts_weights(ctrlptsw):
    """
    Divides weighted control points by weights to generate unweighted control points and weights vector.

    This function is dimension agnostic, i.e. control points can be in any dimension but the last element of the array
    should indicate the weight.

    :param ctrlptsw: weighted control points
    :type ctrlptsw: list, tuple
    :return: unweighted control points and weights vector
    :rtype: list
    """
    ctrlpts = []
    weights = []
    for ptw in ctrlptsw:
        temp = [float(pw / ptw[-1]) for pw in ptw[:-1]]
        ctrlpts.append(temp)
        weights.append(ptw[-1])

    return [ctrlpts, weights]

def flip_ctrlpts_u(ctrlpts, size_u, size_v):
    """
    Flips a list of 1-dimensional control points from u-row order to v-row order.

    **u-row order**: each row corresponds to a list of u values

    **v-row order**: each row corresponds to a list of v values

    :param ctrlpts: control points in u-row order
    :type ctrlpts: list, tuple
    :param size_u: size in u-direction
    :type size_u: int
    :param size_v: size in v-direction
    :type size_v: int
    :return: control points in v-row order
    :rtype: list
    """
    new_ctrlpts = []
    for i in range(0, size_u):
        for j in range(0, size_v):
            temp = [float(c) for c in ctrlpts[i + (j * size_u)]]
            new_ctrlpts.append(temp)

    return new_ctrlpts

def ctrlpts2d_to_ctrlpts(control_points_table):
    """
    Transform the control_points m x n.
    """
    size_u = len(control_points_table)
    size_v = len(control_points_table[0])

    # Make sure that all numbers are float type
    ctrlpts = [[] for _ in range(size_u * size_v)]
    for u in range(size_u):
        for v in range(size_v):
            idx = v + (size_v * u)
            ctrlpts[idx] = [float(coord) for coord in control_points_table[u][v]]

    return ctrlpts

def helper_split_knot_vectors(degree, knotvector, num_ctrlpts, param, span_func):
    """
    Computes knot vectors to split object into two pieces.
    """
    knot_span_new = span_func(degree, knotvector, num_ctrlpts, param) + 1
    kv_1 = list(knotvector[0:knot_span_new])
    kv_1.append(param)
    kv_2 = list(knotvector[knot_span_new:])
    for _ in range(0, degree + 1):
        kv_2.insert(0, param)
    return kv_1, kv_2

def get_knots_and_multiplicities(knotvector):
    """
    Get knots and multiplicities from knotvector in u and v direction.
    """
    knotvector = np.round(knotvector, decimals=17)
    knots = np.unique(knotvector).tolist()
    multiplicities = [core.find_multiplicity(knot, knotvector) for knot in knots]
    return knots, multiplicities

def construct_split_surfaces(obj, knotvectors, direction, knot_span, insertion_count):
    """
    Helper function to construct split surfaces.
    """
    surf1_kv, surf2_kv = knotvectors
    if direction == "u":
        ctrlpts2d = obj.ctrlpts2d()
        surf1_ctrlpts = ctrlpts2d_to_ctrlpts(ctrlpts2d[0:knot_span + insertion_count])
        surf2_ctrlpts = ctrlpts2d_to_ctrlpts(ctrlpts2d[knot_span + insertion_count - 1:])
        u_knots, u_multiplicities = get_knots_and_multiplicities(surf1_kv)
        v_knots, v_multiplicities = get_knots_and_multiplicities(obj.knots_vector_v)
        surf1_nb_u = knot_span + insertion_count
        surf1_nb_v = obj.nb_v
        surf2_nb_u = obj.nb_u - (knot_span + insertion_count - 1)
        surf2_nb_v = obj.nb_v
    else:
        surf1_ctrlpts = []
        surf2_ctrlpts = []
        for v_row in obj.ctrlpts2d():
            temp = v_row[0:knot_span + insertion_count]
            surf1_ctrlpts.extend(temp)
            temp = v_row[knot_span + insertion_count - 1:]
            surf2_ctrlpts.extend(temp)
        u_knots, u_multiplicities = get_knots_and_multiplicities(obj.knots_vector_u)
        v_knots, v_multiplicities = get_knots_and_multiplicities(surf1_kv)
        surf1_nb_u = obj.nb_u
        surf1_nb_v = knot_span + insertion_count
        surf2_nb_u = obj.nb_u
        surf2_nb_v = obj.nb_v - (knot_span + insertion_count - 1)
    weights = None
    if obj.rational:
        surf1_ctrlpts, weights = separate_ctrlpts_weights(surf1_ctrlpts)
    control_points = [volmdlr.Point3D(*point) for point in surf1_ctrlpts]
    surf1 = obj.__class__(obj.degree_u, obj.degree_v, control_points, surf1_nb_u,
                          surf1_nb_v, u_multiplicities, v_multiplicities, u_knots, v_knots, weights)

    # knots
    if direction == "u":
        u_knots, u_multiplicities = get_knots_and_multiplicities(surf2_kv)
    else:
        v_knots, v_multiplicities = get_knots_and_multiplicities(surf2_kv)
    weights = None
    if obj.rational:
        surf2_ctrlpts, weights = separate_ctrlpts_weights(surf2_ctrlpts)
    control_points = [volmdlr.Point3D(*point) for point in surf2_ctrlpts]
    surf2 = obj.__class__(obj.degree_u, obj.degree_v, control_points,
                          surf2_nb_u, surf2_nb_v, u_multiplicities, v_multiplicities, u_knots, v_knots, weights)

    # Return the new surfaces
    return [surf1, surf2]


from rest_framework.views import APIView
from django.http import JsonResponse
from rest_framework.response import Response
from .models import AgeValues, EducationValues, PlaceValues, WeightValues

class DowryView(APIView):
    @staticmethod
    def post(request):
        age = request.data.get('age')
        age_weight = AgeValues.objects.get(start_age__lte=age, end_age__gte=age)
        weight = request.data.get('weight')
        weight_weight = WeightValues.objects.get(start_weight__lte=weight, end_weight__gte=weight)
        education_weight = EducationValues.objects.get(education=request.data['education'])
        place_of_birth_weight = PlaceValues.objects.get(place=request.data['place_of_birth'])

        price = place_of_birth_weight.value * age_weight.value * weight_weight.value * education_weight.value

        return Response({'price': price})

class EducationList(APIView):
    @staticmethod
    def get(request, *args, **kwargs):
        educations = EducationValues.objects.all()
        data = {'educations': [{"name": education.education} for education in educations]}
        return JsonResponse(data, safe=False)

class PlaceList(APIView):
    @staticmethod
    def get(request, *args, **kwargs):
        places = PlaceValues.objects.all()
        data = {'places': [{"name": place.place} for place in places]}
        return JsonResponse(data, safe=False)


from io import BytesIO
import random
from flask import Flask, redirect, render_template, request, url_for, send_file
import numpy as np
from werkzeug.utils import secure_filename
import os
import matplotlib.pyplot as plt
import cv2
import rembg
from scipy import ndimage
app = Flask(__name__)

upload_folder = os.path.join('static', 'uploads')

app.config['UPLOAD'] = upload_folder

@app.route('/')
def home():
    return render_template('home.html')

@app.route('/histogram', methods=['GET', 'POST'])
def histogram_equ():
    if request.method == 'POST':
        file = request.files['img']
        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD'], filename))
        img_path = os.path.join(app.config['UPLOAD'], filename)

        # Membaca gambar dengan OpenCV
        img = cv2.imread(img_path)

        # Menghitung histogram untuk masing-masing saluran (R, G, B)
        hist_r = cv2.calcHist([img], [0], None, [256], [0, 256])
        hist_g = cv2.calcHist([img], [1], None, [256], [0, 256])
        hist_b = cv2.calcHist([img], [2], None, [256], [0, 256])

        # Normalisasi histogram
        hist_r /= hist_r.sum()
        hist_g /= hist_g.sum()
        hist_b /= hist_b.sum()

        # Simpan histogram sebagai gambar PNG
        hist_image_path = os.path.join(app.config['UPLOAD'], 'histogram.png')
        plt.figure()
        plt.title("RGB Histogram")
        plt.xlabel("Value")
        plt.ylabel("Frequency")
        plt.plot(hist_r, color='red', label='Red')
        plt.plot(hist_g, color='green', label='Green')
        plt.plot(hist_b, color='blue', label='Blue')
        plt.legend()
        plt.savefig(hist_image_path)

        # Hasil equalisasi
        # Ubah ke ruang warna YCrCb
        img_equalized = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
        img_equalized[:, :, 0] = cv2.equalizeHist(
            img_equalized[:, :, 0])  # Equalisasi komponen Y (luminance)
        # Kembalikan ke ruang warna BGR
        img_equalized = cv2.cvtColor(img_equalized, cv2.COLOR_YCrCb2BGR)

        # Menyimpan gambar hasil equalisasi ke folder "static/uploads"
        equalized_image_path = os.path.join(
            'static', 'uploads', 'img-equalized.jpg')
        cv2.imwrite(equalized_image_path, img_equalized)

        # Menghitung histogram untuk gambar yang sudah diequalisasi
        hist_equalized_r = cv2.calcHist(
            [img_equalized], [0], None, [256], [0, 256])
        hist_equalized_g = cv2.calcHist(
            [img_equalized], [1], None, [256], [0, 256])
        hist_equalized_b = cv2.calcHist(
            [img_equalized], [2], None, [256], [0, 256])

        # Normalisasi histogram
        hist_equalized_r /= hist_equalized_r.sum()
        hist_equalized_g /= hist_equalized_g.sum()
        hist_equalized_b /= hist_equalized_b.sum()

        # Simpan histogram hasil equalisasi sebagai gambar PNG
        hist_equalized_image_path = os.path.join(
            app.config['UPLOAD'], 'histogram_equalized.png')
        plt.figure()
        plt.title("RGB Histogram (Equalized)")
        plt.xlabel("Value")
        plt.ylabel("Frequency")
        plt.plot(hist_equalized_r, color='red', label='Red')
        plt.plot(hist_equalized_g, color='green', label='Green')
        plt.plot(hist_equalized_b, color='blue', label='Blue')
        plt.legend()
        plt.savefig(hist_equalized_image_path)

        return render_template('histogram_equalization.html', img=img_path, img2=equalized_image_path, histogram=hist_image_path, histogram2=hist_equalized_image_path)

    return render_template('histogram_equalization.html')

def edge_detection(img):
    # Menerapkan deteksi tepi menggunakan algoritma Canny
    edges = cv2.Canny(img, 100, 200)

    # Menyimpan gambar hasil deteksi tepi ke folder "static/uploads"
    edge_image_path = os.path.join(app.config['UPLOAD'], 'edge_detected.jpg')
    cv2.imwrite(edge_image_path, edges)

    return edge_image_path

@app.route('/edge', methods=['GET', 'POST'])
def edge():
    if request.method == 'POST':
        file = request.files['img']
        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD'], filename))
        img_path = os.path.join(app.config['UPLOAD'], filename)

        # Membaca gambar dengan OpenCV
        img = cv2.imread(img_path)

        # Memanggil fungsi edge_detection
        edge_image_path = edge_detection(img)

        return render_template('edge.html', img=img_path, edge=edge_image_path)

    return render_template('edge.html')

def blur_faces(image_path, blur_level):
    # Membaca gambar dengan OpenCV
    img = cv2.imread(image_path)

    # Menggunakan Cascade Classifier untuk mendeteksi wajah
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

    # Menerapkan deteksi wajah dengan parameter yang diatur
    faces = face_cascade.detectMultiScale(
        gray_img, scaleFactor=1.1, minNeighbors=5, minSize=[30, 30])

    # Menerapkan efek blur ke setiap wajah yang terdeteksi
    for (x, y, w, h) in faces:
        # Ambil bagian wajah dari gambar
        face = img[y:y+h, x:x+w]
        # Hitung ukuran kernel berdasarkan tingkat blur yang diatur
        kernel_size = (blur_level, blur_level)
        # Terapkan efek blur Gaussian dengan kernel yang sesuai
        blurred_face = cv2.GaussianBlur(face, kernel_size, 0)
        img[y:y+h, x:x+w] = blurred_face

    # Menyimpan gambar dengan wajah-wajah yang telah di-blur
    blurred_image_path = os.path.join(
        app.config['UPLOAD'], 'blurred_image.jpg')
    cv2.imwrite(blurred_image_path, img)

    return blurred_image_path

@app.route('/faceBlur', methods=['GET', 'POST'])
def face_blur():
    error = None
    if request.method == 'POST':
        # Check if the 'img' file is in the request
        if 'img' not in request.files:
            error = 'Please Select a Picture'
            return render_template('blur.html', error=error)

        file = request.files['img']

        # Check if the file name is empty
        if file.filename == '':
            error = 'Please Select a Picture'
            return render_template('blur.html', error=error)

        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD'], filename))
        image_path = os.path.join(app.config['UPLOAD'], filename)

        # Get blur level from the form
        blur_level = int(request.form.get('tingkatan', 1))

        # Call the function to blur faces
        blurred_image_path = blur_faces(image_path, blur_level)

        return render_template('blur.html', img=image_path, img2=blurred_image_path)

    return render_template('blur.html')

def cartoonize_image(image_path, cartoonized_image_path):
    # Baca gambar
    img = cv2.imread(image_path)

    # Konversi gambar ke grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.medianBlur(gray, 5)

    # Deteksi tepi dengan menggunakan operator Canny
    edges = cv2.adaptiveThreshold(
        gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
    # Dilasi pada gambar tepi
    # edges = cv2.dilate(edges, None, iterations=2)

    # Thresholding gambar untuk mendapatkan gambar kartun
    color = cv2.bilateralFilter(img, 9, 250, 250)
    cartoon = cv2.bitwise_and(color, color, mask=edges)

    # Simpan gambar kartun ke path yang sesuai
    cv2.imwrite(cartoonized_image_path, cartoon)

    # Kembalikan path file gambar kartun
    return cartoonized_image_path

@app.route('/background_remove', methods=['GET', 'POST'])
def background_remove():
    if request.method == 'POST':
        if 'img' not in request.files:
            return render_template('background_remove.html', error='No image file uploaded')

        img_file = request.files['img']

        if img_file.filename == '':
            return render_template('background_remove.html', error='No selected image')

        if img_file:
            input_image_path = os.path.join(
                app.config['UPLOAD'], 'background_input.png')
            output_image_path = os.path.join(
                app.config['UPLOAD'], 'background_output.png')

            img_file.save(input_image_path)
            remove_background(input_image_path, output_image_path)

            return render_template('background_remove.html', input_img=input_image_path, output_img=output_image_path)

    return render_template('background_remove.html')

def remove_background(input_image_path, output_image_path):
    with open(input_image_path, 'rb') as input_file:
        input_data = input_file.read()
        output_data = rembg.remove(input_data)

        with open(output_image_path, 'wb') as output_file:
            output_file.write(output_data)

# Route untuk halaman kartunize

@app.route('/cartoonize', methods=['GET', 'POST'])
def cartoonize():
    if request.method == 'POST':
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        # Memproses gambar menjadi greyscale
        img = cv2.imread(file_path)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        gray = cv2.medianBlur(gray, 5)
        gray_image_path = os.path.join(app.config['UPLOAD'], 'greyscale.jpg')
        cv2.imwrite(gray_image_path, gray)

        # Melakukan deteksi tepi menggunakan operator Threshold
        edges = cv2.adaptiveThreshold(
            gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
        edges_image_path = os.path.join(app.config['UPLOAD'], 'edges.jpg')
        cv2.imwrite(edges_image_path, edges)

        # Mengaplikasikan efek kartun dengan bitwise_and
        color = cv2.bilateralFilter(img, 9, 250, 250)
        cartoon = cv2.bitwise_and(color, color, mask=edges)
        cartoon_image_path = os.path.join(
            app.config['UPLOAD'], 'cartoon_image.jpg')
        cv2.imwrite(cartoon_image_path, cartoon)

        # Mengirim semua hasil gambar ke halaman HTML
        return render_template('cartoonize.html',
                               original=file_path,
                               gray=gray_image_path,
                               edges=edges_image_path,
                               cartoon=cartoon_image_path)
    return render_template('cartoonize.html')

# Tambahan
def detect_and_apply_sticker(image_path):
    # Baca gambar
    img = cv2.imread(image_path)

    # Inisialisasi detektor wajah
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Deteksi wajah
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    # Loop melalui setiap wajah yang terdeteksi
    for (x, y, w, h) in faces:
        # Baca gambar kacamata
        sticker = cv2.imread('kacamata.png', -1)

        # Sesuaikan ukuran kacamata dengan wajah
        sticker = cv2.resize(sticker, (w, h))

        # Tempelkan kacamata pada wajah
        for i in range(sticker.shape[0]):
            for j in range(sticker.shape[1]):
                if sticker[i, j, 3] != 0:
                    img[y+i, x+j] = sticker[i, j, 0:3]

    return img

@app.route('/filter_face', methods=['GET', 'POST'])
def filter_face():
    original_file = None
    filtered_file = None

    if request.method == "POST":
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        # Deteksi wajah dan tempelkan kacamata
        result_image = detect_and_apply_sticker(file_path)
        filter_path = os.path.join(app.config['UPLOAD'], 'filtered.jpg')
        cv2.imwrite(filter_path, result_image)

        # original_file = os.path.join(app.config['UPLOAD'], filename)

        return render_template('filter_face.html', original=file_path, filtered=filter_path)
    return render_template('filter_face.html')

@app.route('/erosion', methods=['GET', 'POST'])
def erosion():

    original_file = None
    filtered_file = None

    if request.method == "POST":
        from io import BytesIO
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        kernel = np.ones((5, 5), 'uint8')
        erosion = cv2.erode(gray, kernel, iterations=1)

        # Deteksi wajah dan tempelkan kacamata
        result_image = BytesIO()
        plt.imsave(result_image, erosion, format='jpg', cmap=plt.cm.gray)
        result_image.seek(0)
        filter_path = os.path.join(app.config['UPLOAD'], 'erosion.jpg')
        with open(os.path.join(app.config['UPLOAD'], 'erosion.jpg'), 'wb') as f:
            f.write(result_image.read())

        return render_template('erosion.html', original=file_path, erosion=filter_path)

    return render_template('erosion.html')

@app.route('/dilatation', methods=['GET', 'POST'])
def dilatation():

    original_file = None
    filtered_file = None

    if request.method == "POST":
        from io import BytesIO
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        kernel = np.ones((5, 5), 'uint8')
        dilate_img = cv2.dilate(gray, kernel, iterations=1)

        # Deteksi wajah dan tempelkan kacamata
        result_image = BytesIO()
        plt.imsave(result_image, dilate_img, format='jpg', cmap=plt.cm.gray)
        result_image.seek(0)
        filter_path = os.path.join(app.config['UPLOAD'], 'dilatation.jpg')
        with open(os.path.join(app.config['UPLOAD'], 'dilatation.jpg'), 'wb') as f:
            f.write(result_image.read())

        return render_template('dilatation.html', original=file_path, dilatation=filter_path)

    return render_template('dilatation.html')

@app.route('/opening', methods=['GET', 'POST'])
def opening():

    original_file = None
    filtered_file = None

    if request.method == "POST":
        from io import BytesIO
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
        opening = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel)

        # Deteksi wajah dan tempelkan kacamata
        result_image = BytesIO()
        plt.imsave(result_image, opening, format='jpg', cmap=plt.cm.gray)
        result_image.seek(0)
        filter_path = os.path.join(app.config['UPLOAD'], 'opening.jpg')
        with open(os.path.join(app.config['UPLOAD'], 'opening.jpg'), 'wb') as f:
            f.write(result_image.read())

        return render_template('opening.html', original=file_path, opening=filter_path)

    return render_template('opening.html')

@app.route('/closing', methods=['GET', 'POST'])
def closing():

    original_file = None
    filtered_file = None

    if request.method == "POST":
        from io import BytesIO
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
        closing = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel)

        # Deteksi wajah dan tempelkan kacamata
        result_image = BytesIO()
        plt.imsave(result_image, closing, format='jpg', cmap=plt.cm.gray)
        result_image.seek(0)
        filter_path = os.path.join(app.config['UPLOAD'], 'closing.jpg')
        with open(os.path.join(app.config['UPLOAD'], 'closing.jpg'), 'wb') as f:
            f.write(result_image.read())

        return render_template('closing.html', original=file_path, closing=filter_path)

    return render_template('closing.html')

@app.route('/rotation', methods=['GET', 'POST'])
def rotation():
    original_file = None
    filtered_file = None

    if request.method == "POST":
        # from io import BytesIO
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        # gray = cv2.cvtColor(img, 1)
        height, width = img.shape[:2]
        degrees = int(request.form['degrees'])
        if degrees in [225, 270]:
            new_height = width
            new_width = height
            center = (height/2, width/3)
            M = cv2.getRotationMatrix2D(center, degrees, 1.0)
            rotated = cv2.warpAffine(img, M, (new_width, new_height))
        elif degrees in [90, 135]:
            new_height = width
            new_width = height
            center = (height/1.335, width/2)
            M = cv2.getRotationMatrix2D(center, degrees, 1.0)
            rotated = cv2.warpAffine(img, M, (new_width, new_height))
        else:
            new_height = height
            new_width = width

            center = (new_width / 2, new_height / 2)
            M = cv2.getRotationMatrix2D(center, degrees, 1.0)
            rotated = cv2.warpAffine(img, M, (new_width, new_height))

        # Deteksi wajah dan tempelkan kacamata

        rotated_path = os.path.join(app.config['UPLOAD'], 'rotate.png')
        cv2.imwrite(rotated_path, rotated)
        return render_template('rotation.html', original=file_path, rotation=rotated_path)
    return render_template('rotation.html')

@app.route('/scale', methods=['GET', 'POST'])
def scale():
    original_file = None
    filtered_file = None

    if request.method == "POST":
        # from io import BytesIO
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)
        percentage = int(request.form['size_value'])

        img = cv2.imread(file_path, 1)
        # gray = cv2.cvtColor(img, 1)

        height, width = img.shape[:2]

        # Hitung ukuran baru berdasarkan persentase
        new_height = int(height * (percentage / 100))
        new_width = int(width * (percentage / 100))

        # Resize gambar
        resized = cv2.resize(img, (new_width, new_height))

        # Deteksi wajah dan tempelkan kacamata

        resized_path = os.path.join(app.config['UPLOAD'], 'resized.png')
        cv2.imwrite(resized_path, resized)
        return render_template('scale.html', original=file_path, scale=resized_path, width=width/5, height=height/5, new_height=new_height/5, new_width=new_width/5)
    return render_template('scale.html')

@app.route('/bilinear', methods=['GET', 'POST'])
def bilinear():
    original_file = None
    filtered_file = None

    if request.method == "POST":
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        percentage = int(request.form.get('size_value'))
        if percentage < 0:
            percentage = 0
        elif percentage > 200:
            percentage = 200

        # Hitung ukuran baru berdasarkan persentase
        new_height = int(img.shape[0] * (percentage / 100))
        new_width = int(img.shape[1] * (percentage / 100))

        # Resize gambar dengan interpolasi linear
        bilinear = cv2.resize(img, (new_width, new_height),
                              interpolation=cv2.INTER_LINEAR)

        # Simpan gambar hasil perubahan skala
        bilinear_path = os.path.join(app.config['UPLOAD'], 'bilinear.png')
        cv2.imwrite(bilinear_path, bilinear)

        # Mode 0 untuk grayscale, 1 untuk RGB
        img_matrix = cv2.imread(bilinear_path, 0)
        img_matrix_list = img_matrix.tolist()

        return render_template('bilinear.html', original=file_path, bilinear=bilinear_path, img2_data=img_matrix_list)

    return render_template('bilinear.html')

@app.route('/bicubic', methods=['GET', 'POST'])
def bicubic():
    original_file = None
    filtered_file = None

    if request.method == "POST":
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        percentage = int(request.form.get('size_value'))
        if percentage < 0:
            percentage = 10
        elif percentage > 200:
            percentage = 200

        # Hitung ukuran baru berdasarkan persentase
        new_height = int(img.shape[1] * (percentage / 100))
        new_width = int(img.shape[1] * (percentage / 100))

        # Resize gambar dengan interpolasi bicubic
        bicubic = cv2.resize(img, (new_width, new_height),
                             interpolation=cv2.INTER_CUBIC)

        # Simpan gambar hasil perubahan skala
        bicubic_path = os.path.join(app.config['UPLOAD'], 'bicubic.png')
        cv2.imwrite(bicubic_path, bicubic)

        # Mode 0 untuk grayscale, 1 untuk RGB
        img_matrix = cv2.imread(bicubic_path, 0)
        img_matrix_list = img_matrix.tolist()

        return render_template('bicubic.html', original=file_path, bicubic=bicubic_path, img2_data=img_matrix_list)

    return render_template('bicubic.html')

def rank_order_filtering(image, cross):

    # Apply median filtering with the cross-shaped structuring element
    result_image = ndimage.median_filter(image, footprint=cross)

    return result_image

# INI yang diubah

def add_salt_and_pepper_noise(image, salt_prob=0.02, pepper_prob=0.02):
    noisy_image = np.copy(image)

    if len(image.shape) == 2:  # Grayscale image
        # Convert to 3 channels for compatibility
        noisy_image = cv2.cvtColor(noisy_image, cv2.COLOR_GRAY2RGB)

    # Menambahkan noise garam (salt)
    num_salt = np.ceil(salt_prob * image.size)
    salt_coords = [np.random.randint(0, i-1, int(num_salt))
                   for i in image.shape[:2]]
    noisy_image[salt_coords[0], salt_coords[1], :] = 1

    # Menambahkan noise lada (pepper)
    num_pepper = np.ceil(pepper_prob * image.size)
    pepper_coords = [np.random.randint(
        0, i-1, int(num_pepper)) for i in image.shape[:2]]
    noisy_image[pepper_coords[0], pepper_coords[1], :] = 0

    return noisy_image
# SAMPE SINI

@app.route('/rank_order_filtering', methods=['GET', 'POST'])
def rank_order_filtering_route():
    if request.method == 'POST':
        # Menerima file gambar yang diunggah oleh pengguna
        uploaded_image = request.files['img']

        if uploaded_image:
            # Simpan file gambar yang diunggah ke direktori sementara
            image_path = 'static/uploaded_image.png'
            uploaded_image.save(image_path)

            # Baca gambar yang diunggah
            image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

            # Tambahkan noise salt-and-pepper
            # noisy_image = add_salt_and_pepper_noise(
            #     image, salt_prob=0.02, pepper_prob=0.02)

            # Create a cross-shaped structuring element (3x3)
            cross = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])

            # Melakukan operasi rank order filtering pada citra
            rank_filtered_image = rank_order_filtering(image, cross)

            # Simpan hasil noisy sebagai gambar
            # noisy_image_path = 'static/noisy_image.png'
            # cv2.imwrite(noisy_image_path, noisy_image)

            # Simpan hasil rank order filtering sebagai gambar
            rank_filtered_image_path = 'static/rank_filtered_image.png'
            cv2.imwrite(rank_filtered_image_path, rank_filtered_image)

            # return render_template('rank_order_filtering.html', original=image_path, noisy_image=noisy_image_path, filtered_image=rank_filtered_image_path)
            return render_template('rank_order_filtering.html', original=image_path, filtered_image=rank_filtered_image_path)

    # Handle the case when 'uploaded_image' is not present in the POST request
    return render_template('rank_order_filtering.html')

def outlier_method(image, cross):

    # Apply median filtering with the cross-shaped structuring element
    result_image = ndimage.median_filter(image, footprint=cross)

    return result_image

@app.route('/outlier', methods=['GET', 'POST'])
def outlier():
    if request.method == 'POST':
        # Menerima file gambar yang diunggah oleh pengguna
        uploaded_image = request.files['img']

        if uploaded_image:
            # Simpan file gambar yang diunggah ke direktori sementara
            image_path = 'static/uploaded_image.png'
            uploaded_image.save(image_path)

            # Baca gambar yang diunggah
            image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

            # Tambahkan noise salt-and-pepper
            noisy_image = add_salt_and_pepper_noise(
                image, salt_prob=0.02, pepper_prob=0.02)

            # Create a cross-shaped structuring element (3x3)
            cross = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])

            # Melakukan operasi outlier method pada citra
            outlier_filtered_image = outlier_method(noisy_image, cross)

            # Simpan hasil noisy sebagai gambar
            noisy_image_path = 'static/noisy_image.png'
            cv2.imwrite(noisy_image_path, noisy_image)

            # Simpan hasil outlier method sebagai gambar
            outlier_filtered_image_path = 'static/outlier_filtered_image.png'
            cv2.imwrite(outlier_filtered_image_path, outlier_filtered_image)

            return render_template('outlier_method.html', original_image=image_path, noisy_image=noisy_image_path, filtered_image=outlier_filtered_image_path)

    # Handle the case when 'uploaded_image' is not present in the POST request
    return render_template('outlier_method.html')

@app.route('/lowpass', methods=['GET', 'POST'])
def lowpass():

    original_file = None
    filtered_file = None

    if request.method == "POST":
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # Ganti kernel dengan Gaussian blur
        # Anda dapat menyesuaikan nilai (5, 5) sesuai kebutuhan
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)

        result_image = BytesIO()
        plt.imsave(result_image, blurred, format='jpg', cmap=plt.cm.gray)
        result_image.seek(0)
        filter_path = os.path.join(app.config['UPLOAD'], 'lowpass.jpg')
        with open(os.path.join(app.config['UPLOAD'], 'lowpass.jpg'), 'wb') as f:
            f.write(result_image.read())

        return render_template('lowpass.html', original=file_path, lowpass=filter_path)

    return render_template('lowpass.html')

@app.route('/median', methods=['GET', 'POST'])
def median():

    original_file = None
    filtered_file = None

    if request.method == "POST":
        file = request.files['img']
        filename = secure_filename(file.filename)
        file_path = os.path.join(app.config['UPLOAD'], filename)
        file.save(file_path)

        img = cv2.imread(file_path, 1)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # Apply Median filter
        blurred = cv2.medianBlur(gray, 5)

        result_image = BytesIO()
        plt.imsave(result_image, blurred, format='jpg', cmap=plt.cm.gray)
        result_image.seek(0)
        filter_path = os.path.join(app.config['UPLOAD'], 'medaian.jpg')
        with open(os.path.join(app.config['UPLOAD'], 'median.jpg'), 'wb') as f:
            f.write(result_image.read())

        return render_template('median.html', original=file_path, median=filter_path)

    return render_template('median.html')

# @app.route('/median', methods=['GET', 'POST'])
# def median_filter():

#     original_file = None
#     median_file = None

#     if request.method == "POST":
#         from io import BytesIO
#         file = request.files['img']
#         filename = secure_filename(file.filename)

#         # Pastikan direktori untuk menyimpan file yang diunggah sudah ada
#         upload_dir = os.path.join(app.static_folder, 'uploads')
#         os.makedirs(upload_dir, exist_ok=True)

#         # Pastikan direktori untuk menyimpan hasil filter sudah ada
#         filter_dir = os.path.join(app.static_folder, 'uploads')
#         os.makedirs(filter_dir, exist_ok=True)

#         file_path = os.path.join(upload_dir, filename)

#         file.save(file_path)

#         img = cv2.imread(file_path, 1)
#         gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#         # Apply Median filter
#         median_filtered = cv2.medianBlur(gray, 5)

#         result_image = BytesIO()
#         plt.imsave(result_image, median_filtered, format='jpg', cmap=plt.cm.gray)
#         result_image.seek(0)
#         median_filename = f'median_{filename}'  # Menggunakan nama file yang berbeda
#         median_path = os.path.join(filter_dir, median_filename)
#         with open(median_path, 'wb') as f:
#             f.write(result_image.read())

#         return render_template('median.html', original=file_path, median=median_path)

#     return render_template('median.html')

if __name__ == '__main__':
    app.run(debug=True, port=8001)


import os
import sys

main_dir = os.path.split(os.getcwd())[0]
result_dir = main_dir + '/results'
sys.path.append(main_dir)

from data import fmri_localizer as newfmri
from model import utils
import numpy as np

x = newfmri.x_indi
y = newfmri.y_target
subs = newfmri.subjects

cv = utils.create_cv(x, y, subs, 4)
cv = np.asarray(cv)
ids = np.arange(len(cv))
np.random.shuffle(ids)
cv = cv[ids]
np.save(main_dir + '/data/cv_newfmri.npy', cv)


from django.contrib import admin

# Register your models here.
from .models import *

class Order_info_detail_Admin(admin.ModelAdmin):

    list_display = ['user',
                    'user_real_name',
                    'goods',
                    'goods_title',
                    'goods_img',
                    'goods_color',
                    'goods_color_img',
                    'goods_size',
                    'goods_amount',
                    'goods_price',
                    'goods_total_price',
                    'orders',
                    'order_time',]

    list_filter = ['user',
                   'order_time',
                   'orders',]

    readonly_fields = ['user',
                       'goods',
                       'goods_color',
                       'goods_size',
                       'goods_amount',
                       'goods_price',
                       'goods_total_price',
                       'orders',
                       'order_time',
                       'goods_img_url',]

class Order_info_Admin(admin.ModelAdmin):
    list_display = ['user',
                    'recipient_name',
                    'recipient_phone_number',
                    'recipient_address',
                    'pay',
                    'order_total_price',
                    'order_time',
                    'order_number',
                    'order_status',
                    'order_check_time',]

    list_filter = ['user',
                   'order_time',
                   'order_number',
                   'order_status']

    # 設置後台管理員不可以修改購物車內容 (只能透過用戶,在頁面操作新增刪除)
    readonly_fields = ['user',
                       'recipient_name',
                       'recipient_phone_number',
                       'recipient_address',
                       'pay',
                       'order_total_price',
                       'order_time',
                       'order_number',]

# 註冊應用
admin.site.register(Order_info_detail, Order_info_detail_Admin)
admin.site.register(Order_info, Order_info_Admin)


# #EXERCICIO 1 - A

# import matplotlib.pyplot as plt
# from IPython.display import display
# import pandas as pd

# #criação do conjunto de dados
# dados = [["investir em mais ações", 562], ["investir em mais títulos", 144], ["não investir", 288], ["investir o mesmo valor de 2012", 461]]
# tabela = pd.DataFrame(dados, columns=["Intenções de investimento", "Frequencia"])
# display(tabela)

# #para criar a página
# figura = plt.figure()

# #para criar o gráfico
# pizza = plt.pie(tabela["Frequencia"], labels=tabela["Intenções de investimento"], autopct='%1.1f%%', shadow=True, startangle=90)

# #para mostrar o gráfico na página
# plt.show()

# #EXERCICIO 1 - B

# import pandas as pd
# import matplotlib.pyplot as plt
# from IPython.display import display

# #criação dos dados
# dados = [["Estados Unidos", 15], ["Itália", 4], ["Etiópia", 2], ["África do Sul", 2], ["Tanzânia", 1], ["Quênia", 9], ["México", 4], ["Marrocos", 1], ["Grã-Bretanha", 1], ["Brasil", 2], ["Nova Zelândia", 1]]
# tabela = pd.DataFrame(dados, columns=["Nacionalidade dos vencedores da prova masculina", "Frequência"])
# display(tabela)

# #criando a página para aparecer o gráfico de pizza
# figura = plt.figure()

# #para criar o gráfico
# pizza = plt.pie(tabela["Frequência"], labels=tabela["Nacionalidade dos vencedores da prova masculina"], autopct='%2.1f%%', shadow=True, startangle=90)

# #para mostrar o gráfico na página
# plt.show()

# EXERCICIO 2

# import pandas as pd
# import matplotlib.pyplot as plt
# from IPython.display import display

# #criação dos dados
# dados = [["América do Norte", 15], ["Europa", 5], ["África", 13], ["América Central",4], ["América do Sul", 2], ["Oceania", 1]]
# tabela = pd.DataFrame(dados, columns=["Nacionalidade dos vencedores da prova masculina", "Frequência"])
# display(tabela)

# #criando a página para aparecer o gráfico de pizza
# figura = plt.figure()

# #para criar o gráfico
# pizza = plt.pie(tabela["Frequência"], labels=tabela["Nacionalidade dos vencedores da prova masculina"], autopct='%2.1f%%', shadow=True, startangle=90)

# #para mostrar o gráfico na página
# plt.show()

#EXERCICIO 3 - A

import pandas as pd
import matplotlib.pyplot as plt

# Criação do conjunto de dados
dados = pd.DataFrame({'Nº de medalhas': [44, 65, 104, 82, 88]})
dados.index = ['Alemanha', 'Grã-Bretanha','Estados Unidos','Rússia','China']

# Organizando os dados em ordem decrescente
dados = dados.sort_values(by='Nº de medalhas', ascending=False)

# Criação do gráfico de Pareto
from matplotlib.ticker import PercentFormatter

# Definição das cores
cor_barra = 'darkviolet'
cor_linha = 'red'
tam_linha = 4

# Construindo o gráfico básico (gráfico de barras)
fig, pareto = plt.subplots() #colocar varios gráficos em uma janela

pareto.bar(dados.index, dados['Nº de medalhas'], color=cor_barra)
pareto.set(xlabel='Países',
           ylabel='Nº de medalhas',
           title = 'Número de medalhas obtidas por atletas de cinco países nos Jogos Olímpicos de Londres - 2012')

# Inserção de rótulos nas barras do gráfico de Pareto
barras = pareto.patches #patches = configuração para modelar as barras
valores = dados['Nº de medalhas']

#zip = pegar dois conjuntos e transformar em um só (1º valor de uma lista com o 1º de outra e junta em um só e asssim suscessivamente)
for barra, valor in zip(barras, valores):
    altura = barra.get_height()
    pareto.text(barra.get_x()+barra.get_width()/2, altura,
                valor, ha='center', va='bottom')

# #para mostrar o gráfico na página
plt.show()

# #EXERCICIO 3 - B


import os
import numpy as np
import pandas as pd
from ics import Calendar, Event
import pytz
from datetime import date, datetime, timedelta
from yaml import safe_load

with open('config.yml',encoding='utf8') as _yaml_file:
    _config = safe_load(_yaml_file)

semester_date_str = _config['semester_date_str']
kouqiangxi_teacher_list = _config['kouqiangxi_teacher_list']
timezone_shanghai = pytz.timezone('Asia/Shanghai')

def generate_ics_file_wrapper(teacher_name):
    xl_path = os.path.join('..','output',semester_date_str,f'{teacher_name}负责课程.xlsx')
    ics_out_path = os.path.join('..','output',semester_date_str,f'{teacher_name}教学日历.ics')
    xlFile = pd.ExcelFile(xl_path)

    dfs = xlFile.sheet_names
    if dfs[0] == 'Sheet1':
        # data_cleaning输出的空白excel文件（当某教师无授课任务的时候会出现）
        print(f'{teacher_name}在{semester_date_str}学期无教学任务，请查证')
    else:
        dfs = [xlFile.parse(s) for s in dfs]
        df = pd.concat(dfs)
        c = Calendar()
        df.apply(lambda row: c.events.add(event_gen(row)), axis=1)

        with open(ics_out_path, 'w', encoding='utf-8') as f:
            f.writelines(c)

def event_gen(para_row):
    para_row.fillna('',inplace=True)
    begin_time = timezone_shanghai.localize(para_row['开始时间'])
    end_time = timezone_shanghai.localize(para_row['结束时间'])
    e = Event(
        name = para_row['课程名称'],
        begin=begin_time,
        end=end_time,
        attendees=para_row['负责人'].split(','),
        location=para_row['地点'],
        description=para_row['备注'],
    )
    return e

for teacher in kouqiangxi_teacher_list:
    generate_ics_file_wrapper(teacher)


# This code was motivated by:
# https://stackoverflow.com/questions/30227466/combine-several-images-horizontally-with-python
# and discussions on plotnine github to combine image

import io
from PIL import Image
import plotnine as p9
import warnings
import numpy as np

def gg2img(gg):
    """
    Convert plotnine ggplot figure to PIL Image and return it

    Arguments:
    ----------
    gg: plotnine ggplot object

    Returns:
    --------
    PIL.Image coversion of the ggplot object

    Details:
    --------
    Associated with the stackoverflow question here: https://stackoverflow.com/questions/8598673/how-to-save-a-pylab-figure-into-in-memory-file-which-can-be-read-into-pil-image/8598881
    """

    buf = io.BytesIO()
    gg.save(buf, format= "png")
    buf.seek(0)
    img = Image.open(buf)
    return img

def h_concat(img_list, reshape = True):
    """
    Horizontally concat PIL.Image images

    Arguments:
    ----------
    img_list: list of PIL.Image images
    reshape: boolean, if we should reshape images to be the same size

    Returns:
    --------
        single PIL.Image image that has the original images in a horizontal row

    Example:
    -------
    test_list = [my_img, my_img, my_img]

    h_concat(test_list)
    """
    if reshape:
        min_shape = sorted( [(np.sum(i.size), i.size ) for i in img_list])[0][1]
        imgs_comb = np.hstack( [np.asarray( i.resize(min_shape) ) for i in img_list] )
    else:
        imgs_comb = np.hstack(img_list)
    imgs_comb = Image.fromarray( imgs_comb)
    return imgs_comb

def v_concat(img_list, reshape = True):
    """
    Vertically concat PIL.Image images

    Arguments:
    ----------
    img_list: list of PIL.Image images
    reshape: boolean, if we should reshape images to be the same size

    Returns:
    --------
        single PIL.Image image that has the original images in a vertically col

    Example:
    -------
    test_list = [my_img, my_img, my_img]

    v_concat(test_list)
    """
    if reshape:
        min_shape = sorted( [(np.sum(i.size), i.size ) for i in img_list])[0][1]
        imgs_comb = np.vstack( [np.asarray( i.resize(min_shape) ) for i in img_list] )
    else:
        imgs_comb = np.vstack(img_list)
    imgs_comb = Image.fromarray( imgs_comb)
    return imgs_comb

def vh_concat(img_list_2d, reshape = True):
    """
    contact a list "matrix" of PIL.Image images

    Arguments:
    ----------
    img_list_2d: 2d list of PIL.Image images (similar to the structure that
        would make a 2d numpy array)
    reshape: boolean, if we should reshape images to be the same size

    Returns:
    --------
        single PIL.Image image that has the original images in the matrix form

    Example:
    -------
    test_list = [[my_img, my_img, my_img],
                 [my_img, my_img, my_img]]

    vh_concat(test_list)
    """
    nrow = len(img_list_2d)
    ncol = len(img_list_2d[0])

    assert np.all([len(row) == ncol for row in img_list_2d]), \
        "img_list_2d should have matrix structure"

    if reshape:
        global_min_shape = [np.Inf, np.Inf]
        for r_idx in range(nrow):
            inner_list = img_list_2d[r_idx]
            inner_min_shape = sorted( [(np.sum(i.size), i.size ) for i in inner_list])[0][1]
            #ipdb.set_trace()
            global_min_shape[0] = np.min([global_min_shape[0], inner_min_shape[0]])
            global_min_shape[1] = np.min([global_min_shape[1], inner_min_shape[1]])

        global_list = []
        for r_idx in range(nrow):
            inner_list = img_list_2d[r_idx]
            global_list.append(
                np.hstack([np.asarray( i.resize(global_min_shape) )
                           for i in inner_list])
            )

        imgs_comb = Image.fromarray(np.vstack(global_list))

    else:
        global_list = []
        for r_idx in range(nrow):
            inner_list = img_list_2d[r_idx]
            global_list.append(np.hstack(inner_list))

        imgs_comb = Image.fromarray(np.vstack(global_list))

    return imgs_comb

def _check_dimensions(n_grobs, nrow = None, ncol = None):
    """
    Internal function to provide non-Null nrow and ncol numbers
    given a n_number of images and potentially some information about the
    desired nrow/ncols.

    Arguments:
    ----------
    n_grobs: int, number of images to be organized
    nrow: int, number of rows user wants (Default is None)
    ncol: int, number of columns user wants (Default is None)

    Returns:
    --------
        (nrow, ncol) tuple that meets user desires or errors if cannot meet
        users expectation
    """
    if nrow is None and ncol is None:
        nrow = int(np.ceil(np.sqrt(n_grobs)))
        ncol = int(np.ceil(n_grobs/nrow))
    if nrow is None:
        nrow = int(np.ceil(n_grobs/ncol))
    if ncol is None:
        ncol = int(np.ceil(n_grobs/nrow))

    assert n_grobs <= nrow * ncol, \
        "nrow * ncol < the number of grobs, please correct"

    return nrow, ncol

def _match_ids(nrow, ncol):
    """
    Internal fucntion to create the a list of images rows and column ids

    Arguments:
    ----------
    nrow: int, number of rows to have
    ncol: int, number of columns to have

    Returns:
    --------
    tuple with:
        row_id: numpy array (nrow*ncol,) with row indices
        col_id: numpy array (nrow*ncol,) with column indices

    """
    row_id = np.repeat(np.arange(nrow, dtype = int),ncol)
    col_id = np.tile(np.arange(nrow, dtype = int),ncol)

    return row_id, col_id

def arrangegrob(grobs, nrow = None, ncol = None, reshape = True,
                suppressWarnings = True):
    """
    Arranges a list of ggplot objects into a set number of rows and columns

    Arguments:
    ----------
    grobs: list of plotnine ggplot objects
    nrow: int, number of rows user wants (Default is None)
    ncol: int, number of columns user wants (Default is None)
    reshape: boolean, if we should reshape images to be the same size
    suppressWarnings: boolean, if we should suppressWarnings from ggplot
        conversion to image

    Returns:
    --------
    single PIL.Image image that has the original images in the matrix form

    Details:
    --------
    Similar to a basic version of R library gridExtra::arrangeGrob function

    """
    # initial setup -----------

    n_grobs = len(grobs)
    nrow, ncol = _check_dimensions(n_grobs, nrow, ncol)
    row_id, _ = _match_ids(nrow, ncol)

    if n_grobs < nrow * ncol:
        n_diff = nrow * ncol - n_grobs
        grobs = grobs + [p9.ggplot(pd.DataFrame()) + p9.theme_void() for _ in range(n_diff)]

    # converting to images -----------
    if suppressWarnings:
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            img_list = [gg2img(gg) for gg in grobs]
    else:
        img_list = [gg2img(gg) for gg in grobs]
    # reshape if necessary ----------

    if reshape:
        min_shape = sorted( [(np.sum(i.size), i.size ) for i in img_list])[0][1]
        img_list = [i.resize(min_shape)  for i in img_list]

    # combining

    global_list = []
    for r_idx in range(nrow):
        inner_img_id = np.ix_(row_id == r_idx)[0]
        inner_img_list = [img_list[good_i] for good_i in inner_img_id]
        global_list.append(np.hstack(inner_img_list))

    single_image = Image.fromarray(np.vstack(global_list))

    return single_image


import jax
import jax.numpy as jnp

from agents.common import construct_minibatches, calculate_gae

def make_train_step(args, network):
    def _update_step(train_state, aux_train_states, traj_batch, last_obs, rng):
        def _update_epoch(update_state, _):
            train_state, traj_batch, advantages, targets, rng = update_state

            def _update_minbatch(train_state, batch_info):
                # --- Update agent ---
                traj_batch, advantages, targets = batch_info

                def _loss_fn(params, traj_batch, gae, targets):
                    pi, value = network.apply(params, traj_batch.obs)
                    log_prob = pi.log_prob(traj_batch.action)

                    # Value loss
                    value_pred_clipped = traj_batch.value + (
                        value - traj_batch.value
                    ).clip(-args.ppo_clip_eps, args.ppo_clip_eps)
                    value_losses = jnp.square(value - targets)
                    value_losses_clipped = jnp.square(value_pred_clipped - targets)
                    value_loss = (
                        0.5 * jnp.maximum(value_losses, value_losses_clipped).mean()
                    )

                    # Actor loss
                    ratio = jnp.exp(log_prob - traj_batch.log_prob)
                    gae = (gae - gae.mean()) / (gae.std() + 1e-8)
                    actor_loss1 = ratio * gae
                    actor_loss2 = (
                        jnp.clip(
                            ratio,
                            1.0 - args.ppo_clip_eps,
                            1.0 + args.ppo_clip_eps,
                        )
                        * gae
                    )
                    actor_loss = -jnp.minimum(actor_loss1, actor_loss2).mean()
                    entropy = pi.entropy().mean()

                    total_loss = (
                        actor_loss
                        + args.value_loss_coef * value_loss
                        - args.entropy_coef * entropy
                    )
                    return total_loss, (value_loss, actor_loss, entropy)

                grad_fn = jax.value_and_grad(_loss_fn, has_aux=True)
                (total_loss, (value_loss, actor_loss, entropy)), grads = grad_fn(
                    train_state.params, traj_batch, advantages, targets
                )
                train_state = train_state.apply_gradients(grads=grads)
                return train_state, {
                    "value_loss": value_loss,
                    "actor_loss": actor_loss,
                    "entropy": entropy,
                }

            # --- Iterate over minibatches ---
            batch = (traj_batch, advantages, targets)
            rng, _rng = jax.random.split(rng)
            minibatches = construct_minibatches(_rng, args, batch)
            train_state, loss = jax.lax.scan(_update_minbatch, train_state, minibatches)
            update_state = (train_state, traj_batch, advantages, targets, rng)
            return update_state, loss

        # --- Calculate advantage ---
        _, last_val = network.apply(train_state.params, last_obs)
        advantages, targets = jax.vmap(calculate_gae, in_axes=(None, 0, 0))(
            args, traj_batch, last_val
        )

        # --- Iterate over epochs ---
        update_state = (train_state, traj_batch, advantages, targets, rng)
        update_state, metrics = jax.lax.scan(
            _update_epoch, update_state, None, args.ppo_num_epochs
        )
        train_state = update_state[0]
        info = traj_batch.info
        metrics = jax.tree_map(lambda x: x.mean(), metrics)

        # No auxiliary networks
        return train_state, aux_train_states, metrics, info

    return _update_step


#!/usr/bin/env python

import yaml

from log_helper import get_logger

l = get_logger('config')

#this class is the parser for yml file

def parser():

	filename = "../settings.yml"

	with open(filename, 'r') as stream:
		try:
			l.info('Parsing Config Files')
			l.info('Opening {}'.format(filename))
			l.info('{} Successfully Read'.format(filename))
			val = yaml.load(stream)
		#	print(val)
		except yaml.YAMLError as exc:
			l.fatal(
                'Error parsing file {filename}: {exc}. Please fix and try '
                'again.'.format(filename=filename, exc=exc))
		#	print(exc)

	return val

def ethosUrl():
	s = parser()
	sa = s['ethosLink']

	l.info('ethOS panel name = ' + sa )
	return str(sa)

if __name__ == "__main__":
	#print (parser())
	s = parser()


#!/usr/bin/python3
"""
queries the Reddit API
"""
import requests

def number_of_subscribers(subreddit):
    """ Use the reddit API
    """
    agent = "Holberton"
    header = {"User-Agent": agent}
    url = "https://www.reddit.com/r/" + subreddit + "/about.json"

    req = requests.get(url, headers=header)

    if req.status_code == 200:
        return req.json().get("data").get("subscribers")
    else:
        return 0


from django import forms
from django.core.exceptions import ValidationError

from .models import *

class CreateRoom(forms.ModelForm):
    c = []

    try:
        types = RoomType.objects.all()
        for t in types:
            c.append((t.pk, t.type_name))
    except:
        pass
    type = forms.ChoiceField(choices=c, widget=forms.Select(attrs = {'class': 'creating_inputs', 'id': 'select_inp'}))
    class Meta:
        model = Rooms
        fields = ['room_name', 'type']
        widgets = {
            'room_name': forms.TextInput(attrs = {'class': 'creating_inputs'}),
        }

    def clean_type(self):
        return RoomType.objects.get(pk=int(self.cleaned_data['type']))

class Search(forms.Form):
    search_inp = forms.CharField(label='', widget=forms.TextInput(attrs={'style': 'width: 70%'}))


import time
import concurrent.futures
import asyncio

def target_function(*args, **kwargs):
    time.sleep(10)
    return sum(args) + sum(kwargs.values())

def evaluate_with_multiprocessing(target_function, num_processes, tasks):
    with concurrent.futures.ProcessPoolExecutor(max_workers=num_processes) as executor:
        results = list(executor.map(target_function, *zip(*tasks)))
    return results

def evaluate_with_multithreading(target_function, num_threads, tasks):
    with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
        results = list(executor.map(target_function, *zip(*tasks)))
    return results

async def evaluate_async(target_function, tasks):
    loop = asyncio.get_event_loop()
    coroutines = [loop.run_in_executor(None, target_function, *task) for task in tasks]
    results = await asyncio.gather(*coroutines)
    return results

def main():
    target_function_name = input("Enter the target function (e.g., 'target_function'): ")
    num_processes = int(input("Enter the number of processes: "))
    num_threads = int(input("Enter the number of threads: "))
    num_tasks = int(input("Enter the number of tasks: "))

    tasks = [(tuple(range(1, num_tasks + 1)), {}) for _ in range(num_tasks)]

    # Using multiprocessing
    start_time = time.time()
    multiprocessing_results = evaluate_with_multiprocessing(
        globals()[target_function_name], num_processes, tasks
    )
    multiprocessing_time = time.time() - start_time

    # Using multithreading
    start_time = time.time()
    multithreading_results = evaluate_with_multithreading(
        globals()[target_function_name], num_threads, tasks
    )
    multithreading_time = time.time() - start_time

    # Using asyncio
    start_time = time.time()
    asyncio_results = asyncio.run(evaluate_async(globals()[target_function_name], tasks))
    asyncio_time = time.time() - start_time

    print("Multiprocessing results:", multiprocessing_results)
    print("Multithreading results:", multithreading_results)
    print("Asyncio results:", asyncio_results)

    print("Time taken for multiprocessing:", multiprocessing_time)
    print("Time taken for multithreading:", multithreading_time)
    print("Time taken for asyncio:", asyncio_time)

if __name__ == "__main__":
    main()


import torch
from pytorch_pretrained_bert import BertTokenizer, BertModel, BertForMaskedLM

bert_tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
bert_model = BertModel.from_pretrained('bert-base-multilingual-cased')
bert_model.eval()
bert_model.to("cuda")

bert_start = "[CLS]"
bert_end = "[SEP]"
bert_vocab_size = 119547
bert_embedding_size = 768

def get_bert_sentence_encoding(sentence_string):
  """
  Input: sentence to be encoded. return last layer encoding for each token in the sentence
  """
  sentence = bert_start + " " + sentence_string + " " + bert_end

  tokenized_text = bert_tokenizer.tokenize(sentence)
  indexed_tokens = bert_tokenizer.convert_tokens_to_ids(tokenized_text)
  segment_ids = [0]*len(tokenized_text)

  tokens_tensor = torch.tensor([indexed_tokens])
  segments_tensors = torch.tensor([segment_ids])

  tokens_tensor = tokens_tensor.to('cuda')
  segments_tensors = segments_tensors.to('cuda')

  with torch.no_grad():
      encoded_layers, _ = bert_model(tokens_tensor, segments_tensors)

  last_encoded_layer = encoded_layers[0][0]

  ret = list()
  ret = [(token, last_encoded_layer[i]) for i, token in enumerate(tokenized_text)]
  return ret#return list of tuples with token and embedding

#example usage
#embedding_tuples = get_bert_sentence_encoding("Das ist ein Beispielsatz.")
#print(embedding_tuples[0])


from pathlib import Path
import parser
import ast
from pprint import pprint
import _ast
import astor

sample_fp = Path("Samples/sample_file_1.py")

class FuncLister(ast.NodeVisitor):
    def visit_FunctionDef(self, node):
        print(node.name)
        self.generic_visit(node)

class RewriteName(ast.NodeTransformer):
    def visit_Name(self, node):
        return ast.copy_location(
            ast.Subscript(
                value=ast.Name(id="data", ctx=ast.Load()),
                slice=ast.Index(value=ast.Str(s=node.id)),
                ctx=node.ctx,
            ),
            node,
        )

tree = RewriteName().visit(tree)

with sample_fp.open() as source:
    tree = ast.parse(source.read())

    # FuncLister().visit(tree)

    for node in ast.walk(tree):
        if isinstance(node, _ast.ClassDef):
            print(node.name)

#     extracted = astor.to_source(tree)
#
#     print(extracted)
#
#     # pprint(tree)
#     # print()
#     # pprint(tree.body)
#     # pprint(type(tree.body))
#     # print()
#
#     for object in tree.body:
#         new_body = []
#         if isinstance(object, _ast.ImportFrom):
#             new_body.append(object)
#             print(f"Found desired object: {object}")
#         elif isinstance(object, _ast.Import):
#             new_body.append(object)
#             print(f"Found desired object: {object}")
#         elif isinstance(object, _ast.ClassDef):
#             new_body.append(object)
#             print(f"Found desired object: {object}")
#         elif isinstance(object, _ast.FunctionDef):
#             new_body.append(object)
#             print(f"Found desired object: {object}")
#         else:
#             pass
#
#     print(new_body)
#
#
# print(astor.dump_tree(tree))