from django.db.models import Q
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.db.models import Count
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, \
    DestroyModelMixin
from rest_framework.generics import GenericAPIView, ListAPIView, UpdateAPIView, CreateAPIView, \
    RetrieveAPIView
from rest_framework.filters import SearchFilter, OrderingFilter, DjangoFilterBackend
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer
from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_202_ACCEPTED, HTTP_204_NO_CONTENT, \
    HTTP_403_FORBIDDEN, HTTP_400_BAD_REQUEST

from ceeb_program.models import UniversitySchool, NonDegreeCategory, NonDegreeCourse, NonDegreeCourseURL, \
    NonDegreeAMPReport
from webtracking.models import WebPage, WebPageScan

from .serializers import UniversitySchoolListSerializer, ReportCreateSerializer, CategorySerializer, \
    ReportListSerializer, ReportSerializer, UniversitySchoolDetailSerializer, SharedReportSerializer, \
    CourseListSerializer, CourseURLListSerializer, AMPReportListSerializer, AMPReportDetailSerializer, \
    ReportUpdateSerializer, UniversitySchoolClientSerializer, UniversitySchoolCategorySerializer, \
    UniversitySchoolCategoryCourseSerializer, NonDegreeWhoopsReportListSerializer, \
    NonDegreeWhoopsReportCreateSerializer, CourseSerializer, CategoryListSerializer
from .pagination import UniversitySchoolPagination, ReportPagination, BasePagination
from .filter import UniversitySchoolFilter, ReportFilter, CourseFilter, CourseURLFilter, AMPReportListFilter, \
    UniversitySchoolCategoryFilter, NonDegreeWhoopsReportFilter, MultipleSearchFilter
from ..models import UniversityCustomer, UpgridAccountManager, NonDegreeReport, NonDegreeSharedReport, \
    NonDegreeWhoopsReport, NonDegreeReportCustomerMapping
import time, datetime

class PermissionMixin(object):
    def is_manager(self):
        manager = UpgridAccountManager.objects.filter(username=self.request.user.username)
        if manager:
            return True
        return False

class UniversitySchoolListAPI(PermissionMixin, ListAPIView):
    """
    Get list of user university school API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = UniversitySchoolListSerializer
    pagination_class = UniversitySchoolPagination
    filter_class = UniversitySchoolFilter

    search_fields = ('ceeb', 'school', 'university_foreign_key__name',)
    ordering_fields = ('ceeb', 'school', 'num_university_costomers', )
    ordering = ('ceeb', 'school', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            university_schools = UniversitySchool.objects.all()\
                .annotate(num_university_costomers=Count('non_degree_user'))
        else:
            university_schools = UniversitySchool.objects.filter(non_degree_user=self.request.user)\
                .annotate(num_university_costomers=Count('non_degree_user'))
        return university_schools

class UniversitySchoolCategoryAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user university school categories API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = UniversitySchoolCategorySerializer
    filter_class = UniversitySchoolCategoryFilter

    search_fields = ('name', )
    ordering_fields = ('name', )
    ordering = ('name', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        categories = NonDegreeCategory.objects.filter(university_school__object_id=self.school_id)
        if not self.is_manager():
            categories = categories.filter(university_school__non_degree_user=self.request.user)
        return categories

    def get(self, request, school_id, *args, **kwargs):
        self.school_id = school_id
        return self.list(request, *args, **kwargs)

class UniversitySchoolCategoryCourseAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user university school courses API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = UniversitySchoolCategoryCourseSerializer
    filter_class = CourseFilter

    search_fields = ('name', )
    ordering_fields = ('name', )
    ordering = ('name', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        courses = NonDegreeCourse.objects.filter(category__university_school__object_id=self.school_id) \
                                         .filter(category__object_id=self.category_id)\
                                         .filter(active=True).distinct()
        if not self.is_manager():
            courses = courses.filter(university_school__non_degree_user=self.request.user)
        return courses

    def get(self, request, school_id, category_id, *args, **kwargs):
        self.school_id = school_id
        self.category_id = category_id
        return self.list(request, *args, **kwargs)

class UniversitySchoolDetailAPI(PermissionMixin, RetrieveAPIView):
    """
    Retrieve school detail API
    """
    lookup_field = 'object_id'
    serializer_class = UniversitySchoolDetailSerializer

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            return UniversitySchool.objects.all()
        return UniversitySchool.objects.none()

class UniversitySchoolClientAPI(PermissionMixin, RetrieveAPIView):
    """
    Retrieve school detail API
    """
    lookup_field = 'object_id'
    serializer_class = UniversitySchoolClientSerializer

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            return UniversitySchool.objects.all()
        return UniversitySchool.objects.none()

class ReportCreateListAPI(PermissionMixin, CreateModelMixin, ListAPIView):
    """
    Get list of user non-degree report API & Create non-degree report API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    pagination_class = ReportPagination
    filter_class = ReportFilter

    search_fields = ('school__school', 'school__ceeb',)
    ordering_fields = ('school', 'date_created')
    ordering = ('school', '-date_created')      # default ordering

    def get_serializer_class(self):
        if self.request.method == 'GET':
            return ReportListSerializer
        else:
            return ReportCreateSerializer

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            reports = NonDegreeReport.objects.all()
        else:
            reports = NonDegreeReport.objects \
                .filter(school__non_degree_user=self.request.user)
        return reports

    @staticmethod
    def create_report(request):
        if 'school' not in request.data:
            raise ValidationError("School object_id is required.")
        try:
            school = UniversitySchool.objects.get(object_id=request.data['school'])
        except UniversitySchool.DoesNotExist:
            raise ValidationError("Can not find school with this object_id.")

        categories = NonDegreeCategory.objects.filter(university_school=school).filter(active=True)
        data = JSONRenderer().render(CategorySerializer(categories, many=True).data)
        return data

    def create(self, request, *args, **kwargs):
        if not self.is_manager():
            return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)

        data = request.POST.copy()
        data['categories'] = self.create_report(request)

        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        report = serializer.save()
        headers = self.get_success_headers(serializer.data)
        customers = UniversityCustomer.objects.filter(non_degree_schools=report.school)
        for customer in customers:
            NonDegreeReportCustomerMapping.objects.create(report=report, customer=customer)
        return Response(serializer.data, status=HTTP_201_CREATED, headers=headers)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class ReportAPI(PermissionMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    """
    Retrieve update and destroy non-degree Report API
    """
    lookup_field = 'object_id'

    def get_serializer_class(self):
        if self.request.method == 'PUT' or self.request.method == 'PATCH':
            return ReportUpdateSerializer
        else:
            return ReportSerializer

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            reports = NonDegreeReport.objects.all()
        else:
            reports = NonDegreeReport.objects \
                .filter(school__non_degree_user=self.request.user)
        return reports

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        if not self.is_manager():
            return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        if not self.is_manager():
            return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)
        return self.destroy(request, *args, **kwargs)

class ReportOverviewMixin(object):
    @staticmethod
    def count_diff(new_report, old_report):
        if old_report is None:
            return {'category_added': 0,
                    'category_removed': 0,
                    'course_added': 0,
                    'course_removed': 0,}

        old_report_dict = {}
        old_report_category = {}
        old_report_course = {}
        category_added = []
        course_added = []
        course_removed = []

        for category in old_report['categories']:
            # Add all category and course to old_report_dict and save their name
            old_report_dict[category['object_id']] = []
            old_report_category[category['object_id']] = category['name']
            for course in category['courses']:
                old_report_dict[category['object_id']].append(course['object_id'])
                old_report_course[course['object_id']] = course['name']

        for category in new_report['categories']:
            if category['object_id'] not in old_report_dict.keys():
                category_added.append(category['name'])
                for course in category['courses']:
                    course_added.append(course['name'])
            else:
                for course in category['courses']:
                    if course['object_id'] not in old_report_dict[category['object_id']]:
                        course_added.append(course['name'])
                    else:
                        old_report_dict[category['object_id']].remove(course['object_id'])
                for course in old_report_dict[category['object_id']]:
                    course_removed.append(old_report_course[course])

                del old_report_category[category['object_id']]
                del old_report_dict[category['object_id']]

        for cat_id, courses in old_report_dict.items():
            for course in courses:
                course_removed.append(old_report_course[course])

        return {'category_added': len(category_added),
                'category_added_name': category_added,
                'category_removed': len(list(old_report_category.values())),
                'category_removed_name': list(old_report_category.values()),
                'course_added': len(course_added),
                'course_added_name': course_added,
                'course_removed': len(course_removed),
                'course_removed_name': course_removed,}

    def count_diff_update(new_report, old_report):
        if old_report is None:
            return {'updated' : 0}

        old_report_category = {}
        old_report_course = {}
        updated = 0

        for category in old_report['categories']:
            old_report_category[category['object_id']] = {}
            old_report_category[category['object_id']]['name'] = category['name']
            for course in category['courses']:
                old_report_course[course['object_id']] = {}
                old_report_course[course['object_id']]['name'] = course['name']
                old_report_course[course['object_id']]['type'] = course['type']
                old_report_course[course['object_id']]['tuition'] = course['tuition_number']
                old_report_course[course['object_id']]['url'] = course['url']
                old_report_course[course['object_id']]['course_dates'] = course['course_dates']

        for category in new_report['categories']:
            if category['object_id'] in old_report_category.keys():
                #compare categories name
                if category['name'] != old_report_category[category['object_id']]['name']:
                    updated += 1
                for course in category['courses']:
                    if course['object_id'] in old_report_course.keys():
                        #compare courses name, type, tuition, schedule, url
                        if course['name'] != old_report_course[course['object_id']]['name']:
                            updated += 1
                        if course['type'] != old_report_course[course['object_id']]['type']:
                            updated += 1
                        if course['tuition_number'] != old_report_course[course['object_id']]['tuition']:
                            updated += 1
                        if course['url'] != old_report_course[course['object_id']]['url']:
                            updated += 1
                        if course['course_dates'] != old_report_course[course['object_id']]['course_dates']:
                            updated += 1
        return {'updated': updated}

class ReportOverview(PermissionMixin, ReportOverviewMixin, APIView):
    """
    Get report overview: category_added, category_removed, course_added, course_removed
    """

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            reports = NonDegreeReport.objects.all()
        else:
            reports = NonDegreeReport.objects \
                .filter(school__non_degree_user=self.request.user)
        return reports

    def get(self, request, new_report_id, old_report_id):
        reports = self.get_queryset()
        new_report = get_object_or_404(reports, object_id=new_report_id)
        old_report = get_object_or_404(reports, object_id=old_report_id)
        new_report_dict = ReportSerializer(new_report).data
        old_report_dict = ReportSerializer(old_report).data
        diff_data = self.count_diff(new_report_dict, old_report_dict)

        return Response(diff_data)

class ReportOverviewLatest(PermissionMixin, ReportOverviewMixin, APIView):
    """
    Get report overview: category_added, category_removed, course_added, course_removed
    """

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            reports = NonDegreeReport.objects.all()
        else:
            reports = NonDegreeReport.objects \
                .filter(school__non_degree_user=self.request.user)
        return reports

    def get(self, request, school_id):
        reports = self.get_queryset()
        school_reports = reports.filter(school=school_id).order_by('-date_created')
        if not school_reports:
            return Response({"Failed": "Report not fund!"}, status=HTTP_400_BAD_REQUEST)
        new_report_dict = ReportSerializer(school_reports.first()).data

        try:
            old_report = school_reports[1]
            old_report_dict = ReportSerializer(old_report).data
        except IndexError:
            old_report_dict = None

        diff_data = self.count_diff(new_report_dict, old_report_dict)

        return Response(diff_data)

class SharedReportCreateAPI(PermissionMixin, CreateAPIView):
    """
    Get list of user non-degree report API
    """

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            reports = NonDegreeReport.objects.all()
        else:
            reports = NonDegreeReport.objects \
                .filter(school__non_degree_user=self.request.user)
        return reports

    def create(self, request, *args, **kwargs):
        time_now = timezone.now()
        if 'expired_day' in request.data:
            if type(request.data['expired_day']) is not int:
                return Response({"Failed": "expired_day must be integer!"}, status=HTTP_400_BAD_REQUEST)
            if request.data['expired_day'] > 30:
                return Response({"Failed": "Expired_day can not greater than 30 days."}, status=HTTP_400_BAD_REQUEST)
            else:
                expired_time = time_now + timezone.timedelta(days=request.data['expired_day'])
        else:
            expired_time = time_now + timezone.timedelta(days=2)

        if 'expired_sec' in request.data:
            if type(request.data['expired_sec']) is not int:
                return Response({"Failed": "expired_sec must be integer!"}, status=HTTP_400_BAD_REQUEST)
            if request.data['expired_sec'] > 60:
                return Response({"Failed": "Expired_sec can not greater than 60 sec."}, status=HTTP_400_BAD_REQUEST)
            else:
                expired_time = expired_time + timezone.timedelta(seconds=request.data['expired_sec'])

        if 'reports' not in request.data:
            return Response({"Failed": "Reports are required."}, status=HTTP_400_BAD_REQUEST)
        if type(request.data['reports']) is not list:
            return Response({"Failed": "reports must be a list of report id!"}, status=HTTP_400_BAD_REQUEST)
        report_list = []
        owned_reports = self.get_queryset()
        for report_id in request.data['reports']:
            try:
                report = owned_reports.get(object_id=report_id)
                report_list.append(report)
            except NonDegreeReport.DoesNotExist:
                return Response({"Failed": "Report does not exist, or you don't have permission to access report."},
                                status=HTTP_400_BAD_REQUEST)

        shared_report = NonDegreeSharedReport.objects.create(created_by=request.user,
                                                             expired_time=expired_time,)

        shared_report.save()
        shared_report.reports.add(*report_list)
        link = '{0}/{1}'.format(shared_report.object_id, shared_report.access_token)
        return Response({'link': link, 'expired_time': shared_report.expired_time}, status=HTTP_201_CREATED)

class SharedReportAPI(PermissionMixin, GenericAPIView):
    """
    Retrieve SharedReport detail API
    """
    permission_classes = (AllowAny,)
    lookup_field = 'object_id'
    serializer_class = SharedReportSerializer
    queryset = NonDegreeSharedReport

    def retrieve(self, request, object_id, access_token, *args, **kwargs):
        shared_report = self.get_object()
        if access_token != str(shared_report.access_token):
            return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)
        if timezone.now() > shared_report.expired_time:
            return Response({"Failed": "Token Expired!"}, status=HTTP_403_FORBIDDEN)
        serializer = self.get_serializer(shared_report)
        return Response(serializer.data)

    def get(self, request, object_id, access_token, *args, **kwargs):
        return self.retrieve(request, object_id, access_token, *args, **kwargs)

class UniversitySchoolCourseAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user non-degree courses
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = CourseListSerializer
    pagination_class = BasePagination
    filter_class = CourseFilter

    search_fields = ('name', )
    ordering_fields = ('name', )
    ordering = ('name', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        courses = NonDegreeCourse.objects.filter(category__university_school__object_id=self.school_id)\
                                         .filter(active=True).distinct()
        if not self.is_manager():
            user = UniversityCustomer.objects.get(id=self.request.user.id)
            courses = courses.filter(category__university_school__in=user.non_degree_schools.all())
        return courses

    def get(self, request, school_id, *args, **kwargs):
        self.school_id = school_id
        return self.list(request, *args, **kwargs)

class CourseURLListAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user non-degree course URL
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = CourseURLListSerializer
    pagination_class = BasePagination
    filter_class = CourseURLFilter

    search_fields = ('url', )
    ordering_fields = ('url', )
    ordering = ('url', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        course_urls = NonDegreeCourseURL.objects.filter(course__object_id=self.course_id)\
            .filter(course__active=True).filter(course__is_advanced_management_program=True)\
            .filter(course__category__university_school__object_id=self.school_id).distinct()
        if not self.is_manager():
            user = UniversityCustomer.objects.get(id=self.request.user.id)
            course_urls = course_urls.filter(course__category__university_school__in=user.non_degree_schools.all())
        return course_urls

    def get(self, request, school_id, course_id, *args, **kwargs):
        self.school_id = school_id
        self.course_id = course_id
        return self.list(request, *args, **kwargs)

class AMPReportListAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user non-degree AMP report
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = AMPReportListSerializer
    pagination_class = BasePagination
    filter_class = AMPReportListFilter

    search_fields = ('webpage__url', )
    ordering_fields = ('webpage__url', 'date_created',)
    ordering = ('-date_created', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        reports = NonDegreeAMPReport.objects.filter(webpage__nondegreecourseurl__object_id=self.url_id)\
            .filter(webpage__nondegreecourseurl__course__object_id=self.course_id)\
            .filter(webpage__nondegreecourseurl__course__category__university_school__object_id=self.school_id)
        if not self.is_manager():
            user = UniversityCustomer.objects.get(id=self.request.user.id)
            reports = reports.filter(webpage__nondegreecourseurl__course__category__university_school__in
                                     =user.non_degree_schools.all())
        return reports

    def get(self, request, school_id, course_id, url_id, *args, **kwargs):
        self.school_id = school_id
        self.course_id = course_id
        self.url_id = url_id
        return self.list(request, *args, **kwargs)

class AMPReportDetailAPI(PermissionMixin, RetrieveModelMixin, DestroyModelMixin, GenericAPIView):
    """
    Get list of user non-degree AMP report detail
    """
    lookup_field = 'object_id'
    serializer_class = AMPReportDetailSerializer

    def get_queryset(self, *args, **kwargs):
        reports = NonDegreeAMPReport.objects.filter(webpage__nondegreecourseurl__object_id=self.url_id)\
            .filter(webpage__nondegreecourseurl__course__object_id=self.course_id)\
            .filter(webpage__nondegreecourseurl__course__category__university_school__object_id=self.school_id)\
            .distinct()
        if not self.is_manager():
            user = UniversityCustomer.objects.get(id=self.request.user.id)
            reports = reports.filter(webpage__nondegreecourseurl__course__category__university_school__in
                                     =user.non_degree_schools.all()).distinct()
        return reports

    def get(self, request, school_id, course_id, url_id, *args, **kwargs):
        self.school_id = school_id
        self.course_id = course_id
        self.url_id = url_id
        return self.retrieve(request, *args, **kwargs)

    def delete(self, request, school_id, course_id, url_id, *args, **kwargs):
        if not self.is_manager():
            return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)
        self.school_id = school_id
        self.course_id = course_id
        self.url_id = url_id
        return self.destroy(request, *args, **kwargs)

class NonDegreeWhoopsReportListAPI(PermissionMixin, ListAPIView):
    """
    Get list of user university school API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
    serializer_class = NonDegreeWhoopsReportListSerializer
    pagination_class = BasePagination
    filter_class = NonDegreeWhoopsReportFilter

    search_fields = ('active',)
    ordering_fields = ('date_created', 'date_modified')
    ordering = ('-date_modified', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            non_degree_whoops = NonDegreeWhoopsReport.objects.all()
        else:
            try:
                user = UniversityCustomer.objects.get(id=self.request.user.id)
            except ObjectDoesNotExist:
                return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)
            non_degree_whoops = NonDegreeWhoopsReport.objects.filter(university_school=user.Ceeb).filter(active=True)
        return non_degree_whoops

class NonDegreeWhoopsReportUpdateAPI(PermissionMixin, UpdateAPIView):
    """
    update NonDegreeWhoopsReport API
    """
    lookup_field = 'object_id'
    serializer_class = NonDegreeWhoopsReportCreateSerializer

    def get_queryset(self, *args, **kwargs):
        if self.is_manager():
            non_degree_whoops = NonDegreeWhoopsReport.objects.all()
        else:
            try:
                user = UniversityCustomer.objects.get(id=self.request.user.id)
            except ObjectDoesNotExist:
                return Response({"Failed": "Permission Denied!"}, status=HTTP_403_FORBIDDEN)
            non_degree_whoops = NonDegreeWhoopsReport.objects.filter(university_school=user.Ceeb).filter(active=True)
        return non_degree_whoops

class CategoryAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user categories API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter, MultipleSearchFilter)
    serializer_class = CategoryListSerializer
    pagination_class = BasePagination
    filter_class = UniversitySchoolCategoryFilter

    search_fields = ('name', )
    multiple_search_fields = ('name', )
    ordering_fields = ('university_school__school', 'name', )
    ordering = ('university_school__school', 'name', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        categories = NonDegreeCategory.objects.filter(active=True)
        if not self.is_manager():
            return categories.filter(university_school__non_degree_user=self.request.user)
        return categories

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

class CourseAPI(PermissionMixin, ListModelMixin, GenericAPIView):
    """
    Get list of user courses API
    """
    filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter, MultipleSearchFilter)
    serializer_class = CourseSerializer
    pagination_class = BasePagination
    filter_class = CourseFilter

    search_fields = ('name', )
    multiple_search_fields = ('name', )
    ordering_fields = ('name', 'university_school__school',)
    ordering = ('university_school__school', 'name', )      # default ordering

    def get_queryset(self, *args, **kwargs):
        courses = NonDegreeCourse.objects.filter(active=True)
        if not self.is_manager():
            courses = courses.filter(university_school__non_degree_user=self.request.user)
        return courses

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

class CourseCountAPI(CourseAPI):
    """
    Get count of user courses API
    """

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        count = queryset.count()
        return Response({'count': count})


# Online Corpus Editor: Backend Server
# Zechy Wong, 19 May 2015 - 30 July 2015

"""
Dependencies
============
* Python 3.4.3

* WebSockets (https://pypi.python.org/pypi/websockets)

* SQLAlchemy (https://pypi.python.org/pypi/SQLAlchemy/1.0.4)

* SQLite with FTS support (https://sqlite.org/fts3.html)
   |- [Bundled] Windows DLL and Linux library with Enhanced Query Syntax
   |- [Bundled] FTS Tokeniser bindings
        (https://github.com/hideaki-t/sqlite-fts-python/)

* PyEnchant (https://pythonhosted.org/pyenchant/)
   |- Also see: https://github.com/rfk/pyenchant/issues/45

* Numpy (http://www.numpy.org/)

* NLTK (http://www.nltk.org/)
   |- Also use the NLTK downloader to install the following NLTK package(s):
       |- punkt

Recommended
===========
Megam (http://www.umiacs.umd.edu/~hal/megam/)
 |- [Bundled] Binaries for Windows, Linux and OS X
Hunspell Dictionaries
 |- [Bundled] Dictionaries for en_GB and en_US (http://wordlist.aspell.net/dicts/)
 |- [Bundled] Dictionary for ms_MY (From the LyX sources: http://www.lyx.org/)

NOTE: Bundled dependencies (in the 'lib' folder) will take precedence over
versions elsewhere on the system.
"""

import argparse
import importlib
import logging
import os
import sys

# ===============
# Path management
# ===============
sys.path.insert(1, os.path.join(os.getcwd(), "lib"))

# ================
# Argument parsing
# ================
parser = argparse.ArgumentParser(
    description="Starts the backend system for the online corpus editor.",
    epilog="Default settings for data providers and client-server interfaces "
           "can be set in 'oce/config.py'.")

# Read in the available providers and interfaces from the configuration file
# and add them as arguments.
import oce.config

for provider, details in oce.config.provider_classes.items():
    parser.add_argument('-{}'.format(provider),
                        default=details['default_source'],
                        help=details['option_help'])

for interface, details in oce.config.interface_classes.items():
    parser.add_argument('-{}'.format(interface),
                        default=details['default_port'],
                        help=details['option_help'])

kwargs = vars(parser.parse_args())

# ====
# Init
# ====
quit_flag = False
while not quit_flag:

    print("\n=== System starting up ===\n")

    # Start up the loader, which tracks imports past this point and marks them
    # for reloading when the server is restarted.
    # The only thing that won't be reloaded is this file.
    loader = importlib.import_module('oce.loader').Loader()

    exceptions = importlib.import_module('oce.exceptions')
    try:
        loader.init(**kwargs)

    # If we see any exceptions, the controller is dead.
    except exceptions.RestartInterrupt:
        print("\n=== Restarting system ===\n")
        loader.unload()
        del loader
        # And loop around to recreate `loader` and reload the system
    except exceptions.ShutdownInterrupt:
        print("\n=== System shut down ===\n")
        quit_flag = True
    except Exception as e:
        print("\n<<< System Error >>>\n")
        raise
    else:
        # The controller went down silently -- This shouldn't happen, but let's
        # log it and leave the system down (in case of infinite loops etc.)
        print("\n<<< Unexpected shutdown >>>\n")
        quit_flag = True
    finally:
        # Reset the logger so that any final log messages (from unexpected
        # errors, etc.) use the basic handler
        root_logger = logging.getLogger()
        root_logger.handlers = []
        logging.basicConfig()


import datetime

import newspaper
import stanza
from newspaper import news_pool, Config
import time

# sources
"""
CNN: Use /year/month/day for articles
CBS: /news
AP: /article
NYT: /year/month/day
WSJ: /news
WP: / *Hard to filter
BBC: /news
ABC: .go.com/SECTION/SECTION || TITLE *Hard to filter
The Atlantic: latest/SECTION/archive/year/month
NBC: /SECTION

"""

# list of sources to map
paper_list = [
    # 'https://cnn.com',  # CNN / Separate finder
    # 'https://www.wsj.com/',  # WSJ // Not extracting
    'https://www.cbsnews.com/',  # CBS
    'https://apnews.com/',  # AP
    'https://www.nytimes.com/',  # NYT
    'https://www.washingtonpost.com/',  # WP
    'https://www.bbc.com/news',  # BBC
    'https://abcnews.go.com/',  # ABC
    'https://www.theatlantic.com/',  # The Atlantic
    'https://www.nbcnews.com/latest-stories',  # NBC
    'https://www.usatoday.com/',  # USA Today
    'https://www.theguardian.com/us',  # The Guardian
    'https://time.com/',  # TIME
    'https://www.npr.org/',  # NPR

]

# config for newspaper build
def get_config():
    config = Config()
    config.memoize_articles = True
    config.fetch_images = False

    return config

# collects newspapers into newspaper Article objects
def collect():
    start_time = time.time()

    # builds paper from news source list. constant build time of articles
    papers = [newspaper.build(paper, config=get_config()) for paper in paper_list]
    # separate CNN collect
    cnn_articles = source_cnn_collect("http://cnn.com")

    news_pool.set(papers, threads_per_source=10)
    news_pool.join()

    article_list = []

    for paper in papers:
        for article in paper.articles:
            article_list.append(article)

    for article in cnn_articles:
        article_list.append(article)

    return article_list

# CNN special collect to filter out other languages and only take today's articles
def source_cnn_collect(cnn_link):
    source_paper = newspaper.build(cnn_link, config=get_config())
    articles = []
    d = datetime.date.today()
    date_format = "/" + str(d.year) + "/" + '{:02d}'.format(d.month) + "/" + '{:02d}'.format(d.day)

    for article in source_paper.articles:
        if cnn_link + date_format in article.url:
            articles.append(article)
            article.download()

    return articles


#python functions recap project
import math

def add(x,y,z):
    print("sum",x+y+z)
    return
add(10,20,30)

def fact(n):

    return(math.factorial(n))

print(fact(4))
print(fact(6))

def student_form():
    institution=(input("Name of institution: "))
    name1=(input("First name: "))
    name2=(input("Second name: "))
    age=(input("Age: "))
    id=(input("Id: "))
    unit=(input("Unit of study: "))
    year=(input("Year: "))
    record=institution,name1,name2,age,id,unit,year
    print( f" Hello {name1} {name2}, your age {age} and Id {id} confirm that you are eligible to join {institution},\n to study {unit} in {year}"  + "\n"
       + str(record))
    return record
student_form()

def flight_booking():
    airport=(input("AirPort :"))
    social_class=(input("Social Class :"))
    price=int(input("Price :"))
    payment_method=(input("Payment Method"))
    date=(input("Date :"))
    seat=(input("Seat :"))
    expiry="23/09/2021"
    approved:bool=True
    ticket=airport,approved,social_class,price,payment_method,date,approved,seat,expiry
    print(f"Hello,{name},welcome to{airport},you have booked for{social_class},at Ksh{price},through{payment_method} is {approved} \n  your seat number is {seat},your flight is on{date},expiry{expiry}")
    return ticket
flight_booking()


"""
Monte Carlo training for 4*4 frozen lake
take about 80s to finish 1000 episodes training
success rate: 79.70%
The shortest route: 6
The longest route: 838

"""
from env4_4 import Environment
from agent_brain4_4 import MonteCarloTable
import numpy as np
import time

# -----Function for generate one episode-----
def monte_carlo_episode():
    episode = []                          # store (s,a,r) of each episode
    s = env.reset()                       # initial observation
    j = 0                                 # if the agent reach the goal j+=1
    t = 0                                 # time of each episode
    start = time.time()                   # record the start time

    while True:
        env.render()                      # refresh the environment
        a = RL.choose_action(str(s))      # choose the action based on observation
        s_, r, done = env.step(a)         # take an action and get the next observation and reward
        episode.append([s, a, r])         # append (St,At+1,Rt+1) into an numpy array
        s = s_                            # swap the observations

        # when agent reach the goal coordinate
        if env.next_state == [350, 350]:
            j += 1                        # j record the times of reaching the goal

        # when agent reached the obstacle or goal
        if done:
            end = time.time()             # record the end time
            t += end - start              # time of each episode
            env.reset()                   # reset the agent
            break
    return episode, t, j

def first_visit(episode):
    episode11 = [i[0] for i in episode]         # extract states of episode
    episode22 = [i[1] for i in episode]         # extract actions of episode
    episode33 = [i[2] for i in episode]         # extract rewards of episode

    # delete the same states and their corresponding actions and rewards
    for i in range(len(episode11)-2, 0, -1):
        for j in range(0, i):
            if episode11[i] == episode11[j]:
                del episode11[i]
                del episode22[i]
                del episode33[i-1]
                break
    return episode11, episode22, episode33

# -----function for the training loop-----
def train():
    reward_history = []         # record average return (St,At)
    steps = []                  # record steps of each episode
    t_ = 0                      # record sum_time
    t_sum = []                  # sum time of training loop
    success_times = 0           # record the times reaching the goal
    Q_sum = []                  # record the Q_sum

    # loop for all episodes
    for i in range(1000):
        episode, t, j = monte_carlo_episode()
        episode1 = [i[0] for i in episode]   # extract the numbers of all steps
        steps.append(len(episode1))          # append numbers of states into a list

        t_ += t                              # summed time
        t_sum.append(t_)                     # append summed time into a list for plot
        success_times += j                   # success times of reaching the goal

        episode11, episode22, episode33 = first_visit(episode)  # first visit MC

        rewards = episode33                  # extract the reward
        r = RL.discounted_rewards(rewards)   # calculate the discounted reward
        Q_sum.append(sum(r))                 # calculate the summed Q_value G<-gamma*G+R(t+1)

        # loop (update the Q table) for one episode
        for k in range(len(episode11)):
            RL.update_table(str(episode11[k]), episode22[k], r[k])
        reward_history.append(np.mean(r))               # append average return (St,At) into a list

    print('success times:', success_times)              # print the success times
    success_rate = success_times / 1000
    print('Success rate: {:.2%}'.format(success_rate))  # show the success rate
    print('running time:', t_)                          # print the simulation time of the algorithm

    env.final()                                         # show the final route

    RL.print_q_table()                                  # show the Q-table

    RL.plot_results(steps, Q_sum, t_sum)                # plot the Q_sum and steps over episodes

if __name__ == "__main__":
    # call for the environment
    env = Environment()
    # input the actions and states to call for the main algorithm
    RL = MonteCarloTable(actions=[0, 1, 2, 3],
                         states=['[50.0, 50.0]', '[50.0, 150.0]', '[50.0, 250.0]', '[50.0, 350.0]',
                                 '[150.0, 50.0]', '[150.0, 150.0]', '[150.0, 250.0]', '[150.0, 350.0]',
                                 '[250.0, 50.0]', '[250.0, 150.0]', '[250.0, 250.0]', '[250.0, 350.0]',
                                 '[350.0, 50.0]', '[350.0, 150.0]', '[350.0, 250.0]', '[350.0, 350.0]'])
    env.after(1000, train)
    env.mainloop()

REINFORCEMENT LEARNING/Kode Program (Jupyter Notebook)/AIM0503D2204 Monte Carlo Prediction/Additional/Monte_Carlo_train4_4.py


import numpy as np
import tensorflow as tf
from PIL import Image
from os import listdir
from os.path import isfile, join

#settings
np.set_printoptions(threshold=np.nan)
unique_characters = 10
number_of_images = 1
number_of_test_images = 1944
training_steps = 100
directory = "/home/ollie/work/fyp"
scaled_image_size = (32,32)

if number_of_images + number_of_test_images > 1945: #1945 0s in dataset
    print("number_of_images + number_of_test_images > 1945")
    quit()

def extract_all(total):
    l = []
    for num in range(total):
        numbers = []
        names = [f for f in listdir(directory + "/digits/" + str(num)) if isfile(join(directory + "/digits/" + str(num), f))]
        total = 0
        for loc in names:
            if total < number_of_images + number_of_test_images:
                with Image.open(directory + "/digits/" + str(num) + "/" + loc) as imageFile:
                    image = imageFile
                    image = image.resize(scaled_image_size,resample = Image.BILINEAR)
                    image = np.array(image)
                    image = image[:, :, 3]
                    image = np.divide(image,255)
                    image = image.flatten()
                    numbers.append(image)
                total += 1
            else:
                break
        print("    "+str(num)+" complete")
        l.append(numbers)
    return l

#import images data
print("Loading raw images...")

all_images = extract_all(unique_characters)

print("Import complete.")

#generate images
print("Generating training images...")

images = []

for i in range(unique_characters):
    images.extend(all_images[i][:number_of_images])

#generate labels
print("Generating training labels...")

def label_generator(num):
    label = [0] * unique_characters
    label[num] = 1
    return label

labels = []
for i in range(unique_characters):
    for j in range(number_of_images):
        labels.append(label_generator(i))

#generate test images
print("Generating test images...")

test_images = []
for i in range(unique_characters):
    test_images.append(all_images[i][number_of_images:number_of_images+number_of_test_images])

#generate test lables

test_labels = []
for i in range(unique_characters):
    for j in range(number_of_test_images):
        test_labels.append(label_generator(i))

#training
def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                        strides=[1, 2, 2, 1], padding='SAME')

# Input layer
x  = tf.placeholder(tf.float32, [None, scaled_image_size[0] * scaled_image_size[1]], name='x')
y_ = tf.placeholder(tf.float32, [None, unique_characters],  name='y_')
x_image = tf.reshape(x, [-1, scaled_image_size[0], scaled_image_size[1], 1])

# Convolutional layer 1
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

# Convolutional layer 2
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])

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

# Fully connected layer 1
h_pool2_flat = tf.reshape(h_pool2, [-1, int(scaled_image_size[0]/4)*int(scaled_image_size[1]/4)*64])

W_fc1 = weight_variable([int(scaled_image_size[0]/4) * int(scaled_image_size[1]/4) * 64, 1024])
b_fc1 = bias_variable([1024])

h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# Dropout
keep_prob  = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# Fully connected layer 2 (Output layer)
W_fc2 = weight_variable([1024, unique_characters])
b_fc2 = bias_variable([unique_characters])

y = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2, name='y')

# Evaluation functions
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))

# Training algorithm
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

# Training steps
sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())

print("Beginning training...")

for step in range(training_steps):
  sess.run(train_step, feed_dict={x: images, y_: labels, keep_prob: 0.5})
  print("    "+str(step)+"/"+str(training_steps), end="\r")

print("Training complete.")
print()

#test

def analyse_result(result):
    results = []
    for i in range(unique_characters):
        results.append(result.count(i))
    return results

def analyse(number):
    numberImages = test_images[number]

    results = []

    for i in range(len(numberImages)):
        results.append(np.argmax(sess.run(y,feed_dict={x: [numberImages[i]], keep_prob: 1.0})))

    analysedResults = analyse_result(results)

    def percentageOfTotal(list,loc):
        return list[loc]/sum(list)*100

    print(str(number) + " - " +str(percentageOfTotal(analysedResults,number))+"% accurate.")
    print("--------------------")
    print("Number | Occurrences")
    current = 0
    for result in analysedResults:
        print(str(current)+"      | "+str(result))
        current += 1
    print("--------------------")
    print()

    return analysedResults[number]

totalCorrect = 0

for i in range(unique_characters):
    totalCorrect += analyse(i)

print("Overall accuracy: "+str(totalCorrect*100/(number_of_test_images*unique_characters))+"%")


class QtProgressBar( object ):

        def __init__( self, progressbar=None,label =None, minValue=0, maxValue=0, total_items=None):
                self.min = minValue
                self.max = maxValue
                self.span = float( self.max - self.min )
                self.fd = fd
                self.signal_set = False

                # This field stores QProgessBar
                self.progressBar = progressbar
                # This field stores the Label
                self.progressLabel = label

                self.value = self.min

                if total_items is None or total_items <= 0:
                        self.items = 0 #count of items being tracked
                        self.items_update = True
                else:
                        self.items = total_items
                        self.items_update = False

                self.complete = 0

        def handle_resize( self, signum, frame ):
                h, w = array( 'h', ioctl( self.fd, termios.TIOCGWINSZ, '\0' * 8 ) )[:2]
                self.width = w

        def updateValue( self, newValue ):
                #require caller to supply a value! newValue is the increment from last call
                self.value = max( self.min, min( self.max, self.value + newValue ) )
                self.display()

        def completed( self ):
                self.complete = self.complete + 1

                #if self.signal_set:
                        #signal.signal( signal.SIGWINCH, signal.SIG_DFL )
                self.display()

        def addItem( self, maxValue ):
                self.max = self.max + maxValue
                self.span = float( self.max - self.min )
                if self.items_update is True:
                        self.items = self.items + 1
                self.display()

        def display( self ):
                #print "\r%3s /%3s items: %s\r" % ( self.complete, self.items, str( self ) ),
                self.progressBar.setValue(int(self.__str__()))
                progressText = "%3s /%3s  Size: %s" % ( self.complete, self.items, self.__numStr__( self.max / 1024 ))
                self.progressLabel.setText(progressText)

        def __str__( self ):
                #compute display fraction
                percentFilled = ( ( self.value - self.min ) / self.span )
                #widthFilled = int( self.width * percentFilled + 0.5 )
                #return ( "[" + "#"*widthFilled + " " * ( self.width - widthFilled ) + "]" + " %5.1f%% of %s" % ( percentFilled * 100.0, self.__numStr__( self.max / 1024 ) ) )
                return percentFilled

        def __numStr__( self, size ):
                if size > 1024:
                        size = size / 1024
                        if size > 1024:
                                size = size / 1024
                                return ( "%d GiB" % ( size ) )
                        return ( "%d MiB" % ( size ) )
                return ( "%d KiB" % ( size ) )


"""datasite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""

from django.contrib import admin
from django.urls import include, path
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path("chpa/", include("chpa_data.urls")),
    path("price_calc/", include("price_calc.urls")),
    path("vbp/", include("vbp.urls")),
    path("rdpac/", include("rdpac.urls")),
    path("forecast/", include("forecast.urls")),
    path("internal_sales/", include("internal_sales.urls")),
    path("potential/", include("potential.urls")),
    path("retail/", include("retail.urls")),
    path("roxa/", include("roxa.urls")),
    path("medical_info/", include("medical_info.urls")),
    path("policy_feed/", include("policy_feed.urls")),
    path("kol/", include("kol.urls")),
    path("nrdl_price/", include("nrdl_price.urls")),
    path("nrdl_renewal/", include("nrdl_renewal.urls")),
    path("admin/", admin.site.urls),
    path("accounts/", include("django.contrib.auth.urls")),
]

# 配置url 当我们访问 settings.MEDIA_URL中的路径时，static会通过document_root去寻找对应的文件
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


# system utilities
import sys

class SimulationManager:
    def __init__(
            self,
            classifier,
            sampler,
            verbose: bool = False
    ):
        self.classifier = classifier
        self.sampler = sampler
        self._logfile = sys.stdout if verbose else open(os.devnull, "w+")

    def run_simulations(self):
        sampled_counts_df, y_true = self.sampler.sample()
        print("Done with sampling", file=self._logfile)
        y_pred = self.classifier.predict(sampled_counts_df)
        print("Done with predicting", file=self._logfile)
        na_stats = sampled_counts_df.isna().mean(axis=0)
        n_rows = sampled_counts_df.shape[0]
        return y_pred, y_true, na_stats, n_rows


'''

-Medium-
*DP*

There is a 3 lane road of length n that consists of n + 1 points labeled from 0 to n. A frog starts at point 0 in the second lane and wants to jump to point n. However, there could be obstacles along the way.

You are given an array obstacles of length n + 1 where each obstacles[i] (ranging from 0 to 3) describes an obstacle on the lane obstacles[i] at point i. If obstacles[i] == 0, there are no obstacles at point i. There will be at most one obstacle in the 3 lanes at each point.

For example, if obstacles[2] == 1, then there is an obstacle on lane 1 at point 2.
The frog can only travel from point i to point i + 1 on the same lane if there is not an obstacle on the lane at point i + 1. To avoid obstacles, the frog can also perform a side jump to jump to another lane (even if they are not adjacent) at the same point if there is no obstacle on the new lane.

For example, the frog can jump from lane 3 at point 3 to lane 1 at point 3.
Return the minimum number of side jumps the frog needs to reach any lane at point n starting from lane 2 at point 0.

Note: There will be no obstacles on points 0 and n.

Example 1:

Input: obstacles = [0,1,2,3,0]
Output: 2
Explanation: The optimal solution is shown by the arrows above. There are 2 side jumps (red arrows).
Note that the frog can jump over obstacles only when making side jumps (as shown at point 2).
Example 2:

Input: obstacles = [0,1,1,3,3,0]
Output: 0
Explanation: There are no obstacles on lane 2. No side jumps are required.
Example 3:

Input: obstacles = [0,2,1,0,3,0]
Output: 2
Explanation: The optimal solution is shown by the arrows above. There are 2 side jumps.

Constraints:

obstacles.length == n + 1
1 <= n <= 5 * 105
0 <= obstacles[i] <= 3
obstacles[0] == obstacles[n] == 0

'''

from typing import List
from random import randint

class Solution:
    def minSideJumps(self, obstacles: List[int]) -> int:
        O, n = obstacles, len(obstacles)-1
        dp = [[float('inf')]*3 for _ in range(n+1)]
        dp[n][0] = dp[n][1] = dp[n][2] = 0
        for i in range(n-1, -1, -1):
            o = O[i] - 1
            if o != 0:
               dp[i][0] = min(dp[i+1][0], 1+dp[i+1][1] if o != 1 else float('inf'), 1+dp[i+1][2] if o != 2 else float('inf'))
            if o != 1:
               dp[i][1] = min(dp[i+1][1], 1+dp[i+1][0] if o != 0 else float('inf'), 1+dp[i+1][2] if o != 2 else float('inf'))
            if o != 2:
               dp[i][2] = min(dp[i+1][2], 1+dp[i+1][1] if o != 1 else float('inf'), 1+dp[i+1][0] if o != 0 else float('inf'))
        # for i in range(3):
        #     for j in range(n+1):
        #         print(dp[j][i],end=' ')
        #     print("\n")
        return dp[0][1]

    def minSideJumps2(self, obstacles: List[int]) -> int:
        O, n = obstacles, len(obstacles)-1
        dp = [[float('inf')]*3 for _ in range(n+1)]
        dp[n][0] = dp[n][1] = dp[n][2] = 0
        for i in range(n-1, -1, -1):
            o = O[i] - 1
            if o == -1:
                dp[i][0] = min(dp[i+1][0], 1+dp[i+1][1], 1+dp[i+1][2])
                dp[i][1] = min(dp[i+1][1], 1+dp[i+1][0], 1+dp[i+1][2])
                dp[i][2] = min(dp[i+1][2], 1+dp[i+1][1], 1+dp[i+1][0])
            elif o == 0:
                dp[i][1] = min(dp[i+1][1], 1+dp[i+1][2])
                dp[i][2] = min(dp[i+1][2], 1+dp[i+1][1])
            elif o == 1:
                dp[i][0] = min(dp[i+1][0], 1+dp[i+1][2])
                dp[i][2] = min(dp[i+1][2], 1+dp[i+1][0])
            else:
                dp[i][0] = min(dp[i+1][0], 1+dp[i+1][1])
                dp[i][1] = min(dp[i+1][1], 1+dp[i+1][0])
        return dp[0][1]

    def minSideJumps3(self, obstacles: List[int]) -> int:
        O, n = obstacles, len(obstacles)-1
        dp_0 = [0]*3
        dp_1 = [0]*3
        for i in range(n-1, -1, -1):
            o = O[i] - 1
            if o == -1:
                dp_1[0] = min(dp_0[0], 1+dp_0[1], 1+dp_0[2])
                dp_1[1] = min(dp_0[1], 1+dp_0[0], 1+dp_0[2])
                dp_1[2] = min(dp_0[2], 1+dp_0[1], 1+dp_0[0])
            elif o == 0:
                dp_1[0] = float('inf')
                dp_1[1] = min(dp_0[1], 1+dp_0[2])
                dp_1[2] = min(dp_0[2], 1+dp_0[1])
            elif o == 1:
                dp_1[1] = float('inf')
                dp_1[0] = min(dp_0[0], 1+dp_0[2])
                dp_1[2] = min(dp_0[2], 1+dp_0[0])
            else:
                dp_1[2] = float('inf')
                dp_1[0] = min(dp_0[0], 1+dp_0[1])
                dp_1[1] = min(dp_0[1], 1+dp_0[0])
            dp_0, dp_1 = dp_1, dp_0
            # print(i, dp_0)
        return dp_0[1]

    def minSideJumps4(self, obstacles: List[int]) -> int:
        O, n = obstacles, len(obstacles)-1
        dp = [0, 0, 0]
        for i in range(n-1, -1, -1):
            if O[i]:
                dp[O[i]-1] = float('inf')
            for k in range(3):
                if O[i] != k+1:
                    dp[k] = min(dp[k], 1+dp[(k+1)%3], 1+dp[(k+2)%3])
        return dp[1]

if __name__ == "__main__":
    print(Solution().minSideJumps(obstacles = [0,1,2,3,0]))
    print(Solution().minSideJumps3(obstacles = [0,1,2,3,0]))
    print(Solution().minSideJumps4(obstacles = [0,1,2,3,0]))
    print(Solution().minSideJumps(obstacles = [0,1,1,3,3,0]))
    print(Solution().minSideJumps4(obstacles = [0,1,1,3,3,0]))
    print(Solution().minSideJumps(obstacles = [0,2,1,0,3,0]))
    print(Solution().minSideJumps4(obstacles = [0,2,1,0,3,0]))
    N = 5*10**5
    obstacles = []
    for i in range(N):
        obstacles.append(randint(0, 3))
    obstacles[0] = 0
    obstacles[N-1] = 0
    # print(Solution().minSideJumps(obstacles = obstacles))
    # print(Solution().minSideJumps2(obstacles = obstacles))
    print(Solution().minSideJumps3(obstacles = obstacles))
    print(Solution().minSideJumps4(obstacles = obstacles))


import os

from django.utils.translation import ugettext_lazy as _

BUILT_IN_TAGS = [_("主机"), _("消息中间件"), _("HTTP服务"), _("数据库"), _("办公应⽤"), _("其他")]

HEARTBEAT_MESSAGE_ID = 101178

BEAT_RUN_ERR = 4001  # 脚本运行报错
BEAT_PARAMS_ERR = 4002  # 脚本命令不合法
BEAT_FORMAT_OUTPUT_ERR = 4003  # 解析脚本标准输出报错

BEAT_ERR = {
    BEAT_RUN_ERR: _("脚本运行报错,原因是{}"),
    BEAT_PARAMS_ERR: _("脚本命令不合法,原因是{}"),
    BEAT_FORMAT_OUTPUT_ERR: _("解析脚本标准输出报错,原因是{}"),
}

OS_TYPE_TO_DIRNAME = {
    "windows": "external_plugins_windows_x86_64",
    "linux": "external_plugins_linux_x86_64",
    "linux_aarch64": "external_plugins_linux_aarch64",
    "aix": "external_plugins_aix_powerpc",
}
OS_TYPE_TO_SCRIPT_SUFFIX = {"shell": ".sh", "bat": ".bat", "custom": ""}

INNER_DIMENSIONS = [
    "bk_biz_id",
    "bk_target_ip",
    "bk_target_cloud_id",
    "bk_target_topo_level",
    "bk_target_topo_id",
    "bk_target_service_category_id",
    "bk_target_service_instance_id",
    "bk_collect_config_id",
]

PLUGIN_TEMPLATES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "plugin_templates")

# config_json 字段类型选项
class ParamMode(object):
    # 采集器参数
    COLLECTOR = "collector"
    # 命令行选项参数
    OPT_CMD = "opt_cmd"
    # 命令行位置参数
    POS_CMD = "pos_cmd"
    # 环境变量
    ENV = "env"
    # 维度注入
    DMS_INSERT = "dms_insert"

PARAM_MODE_CHOICES = [
    ParamMode.COLLECTOR,
    ParamMode.OPT_CMD,
    ParamMode.POS_CMD,
    ParamMode.ENV,
    ParamMode.DMS_INSERT,
]

SCRIPT_TYPE_CHOICES = (
    "shell",
    "bat",
    "python",
    "perl",
    "powershell",
    "vbs",
    "custom",
)

PROCESS_MATCH_TYPE_CHOICES = (
    "pid",
    "command",
)

class NodemanRegisterStatus(object):
    FAILED = "FAILED"
    RUNNING = "RUNNING"
    SUCCESS = "SUCCESS"

class DebugStatus(object):
    INSTALL = "INSTALL"
    FETCH_DATA = "FETCH_DATA"
    FAILED = "FAILED"
    SUCCESS = "SUCCESS"

class PluginType(object):
    EXPORTER = "Exporter"
    SCRIPT = "Script"
    JMX = "JMX"
    DATADOG = "DataDog"
    PUSHGATEWAY = "Pushgateway"
    BUILT_IN = "Built-In"
    LOG = "Log"
    PROCESS = "Process"
    SNMP_TRAP = "SNMP_Trap"
    SNMP = "SNMP"

class ConflictMap(object):
    """用于插件导入的冲突提示，此处国际化在调用方进行"""

    class VersionBelow(object):
        id = 1
        info = _("导入版本不大于当前版本")

    class PluginType(object):
        id = 2
        info = _("插件类型冲突")

    class RemoteCollectorConfig(object):
        id = 3
        info = _("远程采集配置项冲突")

    class RelatedCollectorConfig(object):
        id = 4
        info = _("插件已关联%s个采集配置")

    class DuplicatedPlugin(object):
        id = 5
        info = _("导入插件与当前插件内容完全一致")

SUPPORT_REMOTE_LIST = [PluginType.PUSHGATEWAY, PluginType.JMX, PluginType.SNMP]

# SNMP Trap插件默认运行参数
DEFAULT_TRAP_CONFIG = {
    "server_port": "162",
    "listen_ip": "0.0.0.0",
    "yaml": {"filename": "", "value": ""},
    "community": "",
    "aggregate": True,
}
DEFAULT_TRAP_V3_CONFIG = {
    "version": "v3",
    "auth_info": [
        {
            "security_level": "noAuthNoPriv",
            "security_name": "",
            "context_name": "",
            "authentication_protocol": "MD5",
            "authentication_passphrase": "",
            "privacy_protocol": "DES",
            "privacy_passphrase": "",
            "authoritative_engineID": "",
        }
    ],
}

SNMP_MAX_METRIC_NUM = 500
MAX_METRIC_NUM = 2000

PLUGIN_REVERSED_DIMENSION = [
    ("bk_target_ip", _("目标IP")),
    ("bk_target_cloud_id", _("云区域ID")),
    ("bk_target_topo_level", _("拓扑层级")),
    ("bk_target_topo_id", _("拓扑ID")),
    ("bk_target_service_category_id", _("服务类别ID")),
    ("bk_target_service_instance_id", _("服务实例")),
    ("bk_collect_config_id", _("采集配置ID")),
    ("bk_target_host_id", _("目标主机ID")),
    ("bk_host_id", _("采集主机ID")),
    ("time", _("数据上报时间")),
    ("bk_biz_id", _("业务ID")),
    ("bk_supplier_id", _("开发商ID")),
    ("bk_cloud_id", _("采集器云区域ID")),
    ("ip", _("采集器IP")),
    ("bk_cmdb_level", _("CMDB层级信息")),
    ("bk_agent_id", _("Agent ID")),
]

ORIGIN_PLUGIN_EXCLUDE_DIMENSION = [
    "bk_host_id",
    "bk_target_host_id",
    "bk_biz_id",
    "ip",
    "time",
    "bk_supplier_id",
    "bk_cloud_id",
    "bk_cmdb_level",
    "bk_agent_id",
]


# Receba um numero inteiro e, a a partir de funções, imprima a tubuada desse numero de acordo com  vontade do operador
# O operador poderá escolher a tubuada da soma, subtração, divisão, multiplicação ou todas
# Inclua um metodo para que não seja quebrado o programado e adicione uma opção de saída para o operador
# OBS: Cuidado com a tabuada da divisão, adicione somente duas casas decimais
import random

def main():
    import random
    global num
    global operador
    global novoOperador
    operador = ['*', '/', '-', '+', '* e / e + e -']
    novoOperador = random.choice(operador)
    print(f"Seu operador é: {novoOperador}")

    num = int(input("Digite um numero inteiro: "))

    if novoOperador == '*':
          multiplicacao()
    elif novoOperador == '/':
          divisao()
    elif novoOperador == '-':
          subtracao()
    elif novoOperador == '+':
          adicao()
    elif novoOperador == '* e / e + e -':
         todas()
    else:
          print("Erro")

def multiplicacao():
    print("Multiplicação: ")
    for i in range(11):

        print(f"{num} * {i} = {num * i}")
'''
    print(f"{num} * 1",num * 1)
    print(f"{num} * 2",num * 2)
    print(f"{num} * 3",num * 3)
    print(f"{num} * 4",num * 4)
    print(f"{num} * 5",num * 5)
    print(f"{num} * 6",num * 6)
    print(f"{num} * 7",num * 7)
    print(f"{num} * 8",num * 8)
    print(f"{num} * 9",num * 9)
    print(f"{num} * 10",num * 10)
'''
def divisao():
      print("Divisão: ")

      for i in range(1,11):

        print(f"{num} / {i} = {num / i}")
'''
print(f"{num} * 1 = {(num / 1):.2f}")
print(f"{num} * 2 = {(num / 2):.2f}")
print(f"{num} * 3 = {(num / 3):.2f}")
print(f"{num} * 4 = {(num / 4):.2f}")
print(f"{num} * 5 = {(num / 5):.2f}")
print(f"{num} * 6 = {(num / 6):.2f}")
print(f"{num} * 7 = {(num / 7):.2f}")
print(f"{num} * 8 = {(num / 8):.2f}")
print(f"{num} * 9 = {(num / 9):.2f}")
print(f"{num} * 10 = {(num / 10):.2f}")
'''

def subtracao():
    print("Subtração: ")
    for i in range(11):

        print(f"{num} - {i} = {num - i}")
'''
    print(num - 1)
    print(num - 2)
    print(num - 3)
    print(num - 4)
    print(num - 5)
    print(num - 6)
    print(num - 7)
    print(num - 8)
    print(num - 9)
    print(num - 10)
'''
def adicao():
    print("Adição: ")
    for i in range(11):

        print(f"{num} + {i} = {num + i}")
'''
    print(num + 1)
    print(num + 2)
    print(num + 3)
    print(num + 4)
    print(num + 5)
    print(num + 6)
    print(num + 7)
    print(num + 8)
    print(num + 9)
    print(num + 10)
'''
def todas():
        adicao()
        subtracao()
        divisao()
        multiplicacao()

main()


# 1. [무료배송] 에어팟프로 애플로고 실리콘 케이스
# 2. 도낫도낫 와일드 에어팟 프로 실리콘 케이스
# 3. 누아트 페블 에어팟프로 실리콘케이스 + 메탈 철가루 방지스티커 랜덤 발송

import random
import argparse
import sys
# from time import time
# import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim

import numpy as np
import torch.nn.functional as F
from utils import *

class FocalLoss(nn.Module):
    def __init__(self, alpha=0.25, gamma=2, logits=False, reduce=True):
        super(FocalLoss, self).__init__()
        self.alpha = alpha
        self.gamma = gamma
        self.logits = logits
        self.reduce = reduce

    def forward(self, inputs, targets):
        # ce_loss = nn.BCELoss()(inputs, targets)
        BCE = nn.BCEWithLogitsLoss()
        ce_loss = BCE(inputs, targets)

        pt = torch.exp(-ce_loss)
        F_loss = self.alpha * (1 - pt) ** self.gamma * ce_loss

        return torch.mean(F_loss)

class Dice_loss(nn.Module):
    def __init__(self, weight=None, size_average=True):
        super(Dice_loss, self).__init__()

    def forward(self, inputs, targets, smooth=1):
        # comment out if your model contains a sigmoid or equivalent activation layer
        bce_ = nn.BCEWithLogitsLoss(reduction='mean')
        bce = bce_(inputs, targets)

        inputs = F.sigmoid(inputs)

        # flatten label and prediction tensors
        inputs = inputs.view(-1)
        targets = targets.view(-1)

        intersection = (inputs * targets).sum()
        dice = (2. * intersection + smooth) / (inputs.sum() + targets.sum() + smooth)
        loss = (1 - dice) + bce

        return loss, bce, 1 - dice

def get_init():
    parser = argparse.ArgumentParser()

    # ETC
    # parser.add_argument('--data_path', default='/home/sam/Desktop/RM/datasets')
    parser.add_argument('--data_path', default='../../datasets')
    parser.add_argument('--split_ratio', type=float, default=0.2)
    parser.add_argument('--parent_dir', type=str, default='./saved_models/', help='for saving trained models')
    parser.add_argument('--num_workers', type=int, default=8)
    parser.add_argument('--save_root', default='SE_epoch100_coverage', help='res18, res101rfamdec, SE_mul_erosion')
    parser.add_argument('--train', action='store_true', default=True) # python3 main.py --train => 학습하는 거고 | python3 main.py => 테스트 하는 거임 ㅇㅋ 입력
    parser.add_argument('--saved_pt', type=str, default='last_100.pth')

    # Train Parser Args
    parser.add_argument('--model_name', default='thorn', help='res18, thorn, res18_base, res18worms, res50, res101, resnext101, vgg16, vgg16_seg')
    parser.add_argument('--num_labels', type=int, default=1)
    parser.add_argument('--optimizer', type=str, default='Adam', help='Adam, SGD, adabound')
    parser.add_argument('--momentum', type=float, default=0.9)
    parser.add_argument('--weight_decay', type=float, default=0.001)
    parser.add_argument('--criterion', type=str, default='DICE', help='BCE, CE(2) FL, DICE')
    parser.add_argument('--trainer', default='fuse', help='fuse, base, MFCN, split, autp, trans, SE, worms')
    parser.add_argument('--dataloader', default='casia1', help='dso1, coverage, casia2, casia1')

    parser.add_argument('--aug_option', default='n', help='')
    parser.add_argument('--erase_prob', type=float, default=0.0)
    parser.add_argument('--patch_size', type=int, default=5)
    parser.add_argument('--diagonal', type=int, default=100)#, default=int(np.random.randint(256, size=1)), help=' int(np.random.randint(256, size=1)) , 100')
    parser.add_argument('--load', type=str, default='f')

    parser.add_argument('--img_size', default=256, type=int, help='image width and height size')
    parser.add_argument('--batch_size', type=int, default=32)
    parser.add_argument('--epochs', type=int, default=100)
    parser.add_argument('--learning_rate', type=float, default=0.001)
    # parser.add_argument('--prune', type=float, default=0.5)

    args = parser.parse_args()
    return args

def print_configurations(args) :
    print("+++++++++++++++++++++++++++++++++++")
    print("data_type : {}".format(args.dataloader))
    print("save_root : {}".format(args.save_root))
    print("model_name : {}".format(args.model_name))
    print("optimizer : {}".format(args.optimizer))
    print("learning_rate : {}".format(args.learning_rate))
    print("momentum : {}".format(args.momentum))
    print("weight_decay : {}".format(args.weight_decay))
    print("criterion : {}".format(args.criterion))
    print("batch_size : {}".format(args.batch_size))
    print("epochs : {}".format(args.epochs))
    print("img_size : {}".format(args.img_size))
    print("+++++++++++++++++++++++++++++++++++")

def fix_seed(device) :
    random.seed(4321)
    np.random.seed(4321)
    torch.manual_seed(4321)
    if device == 'cuda':
        torch.cuda.manual_seed_all(4321)
        torch.backends.cudnn.deterministic = True
        torch.backends.cudnn.benchmark = False

    print(f"Your experiment is fixed to {4321}")

def get_trainer(trainer):
    if trainer == 'fuse':
        from Trainer_fusion import fit, test_epoch
    elif trainer == 'autp':
        from Trainer_autp import fit, test_epoch
    elif trainer == 'tune':
        from Trainer_tune import fit, test_epoch
    elif trainer == 'worms':
        from Trainer_worms import fit, test_epoch
    elif trainer == 'SE':
        from Trainer_SE import fit, test_epoch
    elif trainer == 'basic':
        from Trainer_Basic import fit, test_epoch
    elif trainer == 'MFCN':
        from Trainer_MFCN import fit, test_epoch
    return fit, test_epoch

def get_dataloader(args):
    # if args.dataloader == 'autp':
    #     from utils.load_functions import load_autp
    #     train_loader, test_loader = load_autp(data_path=args.data_path,
    #                                             split_ratio=args.split_ratio,
    #                                             batch_size=args.batch_size,
    #                                             img_size=args.img_size,
    #                                             num_workers=args.num_workers)
    if args.dataloader == 'casia2':
        from utils.load_functions import load_casia2
        train_loader, test_loader = load_casia2(data_path=args.data_path,
                                                split_ratio=args.split_ratio,
                                                batch_size=args.batch_size,
                                                img_size=args.img_size,
                                                num_workers=args.num_workers)
    elif args.dataloader == 'casia1':
        from utils.load_functions import load_casia1
        train_loader, test_loader = load_casia1(data_path=args.data_path,
                                                split_ratio=args.split_ratio,
                                                batch_size=args.batch_size,
                                                img_size=args.img_size,
                                                num_workers=args.num_workers)
    elif args.dataloader == 'dso1':
        from utils.load_functions import load_dso1
        train_loader, test_loader = load_dso1(data_path=args.data_path,
                                                split_ratio=args.split_ratio,
                                                batch_size=args.batch_size,
                                                img_size=args.img_size,
                                                num_workers=args.num_workers)
    elif args.dataloader == 'coverage':
        from utils.load_functions import load_coverage
        train_loader, test_loader = load_coverage(data_path=args.data_path,
                                                split_ratio=args.split_ratio,
                                                batch_size=args.batch_size,
                                                img_size=args.img_size,
                                                num_workers=args.num_workers)

    return train_loader, test_loader

def get_device() :
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    print(f"your device is {device}")
    return device

def get_current_lr(optimizer) :
    for param_group in optimizer.param_groups:
        return param_group['lr']

def get_lr(step, total_step, lr_max, lr_min) :
    """Compute learning rate according to cosine annealing schedule."""
    return lr_min + (lr_max - lr_min) * 0.5 * ( 1 + np.cos(step/total_step * np.pi))

def get_criterion(args) :
    if args.criterion == 'BCE' :
        criterion = nn.BCEWithLogitsLoss()
    elif args.criterion == 'CE':
        criterion = nn.CrossEntropyLoss()
    elif args.criterion == 'FL' :
        criterion = FocalLoss()
    elif args.criterion == 'DICE':
        criterion = Dice_loss()
    elif args.criterion == 'Region':
        from models.model_core.region_loss import Region_loss
        criterion = Region_loss()
    else :
        print("You choose wrong criterion : [BCE, FL, DICE]")
        sys.exit()

    print("Your criterion is : ", criterion)

    return criterion

def get_optimizer(args, model) :
    if args.optimizer == 'Adam' :
        optimizer = optim.Adam(model.parameters(), lr=args.learning_rate)
    elif args.optimizer == 'SGD' :
        optimizer = optim.SGD(model.parameters(),
                              lr=args.learning_rate,
                              momentum=args.momentum,
                              weight_decay=args.weight_decay,
                              nesterov=True)
    elif args.optimizer == 'adabound':
        import adabound
        optimizer = adabound.AdaBound(model.parameters(), lr=args.learning_rate, final_lr=0.1)

    else :
        print("You choose wrong optimizer : [Adam, SGD]")
        sys.exit()

    print("Your optimizer is : ", optimizer)

    return optimizer

def get_scheduler(args, train_loader, optimizer) :
    scheduler = torch.optim.lr_scheduler.LambdaLR(
        optimizer,
        lr_lambda=lambda step: get_lr(  # pylint: disable=g-long-lambda
            step,
            args.epochs * len(train_loader),
            1,  # lr_lambda computes multiplicative factor
            1e-6 / args.learning_rate))

    return scheduler

def get_history() :
    history = dict()
    history['train_loss'] = list()
    history['test_loss'] = list()

    return history

def get_patches(image, channel, patch_size):
    unfold = torch.nn.Unfold(kernel_size=patch_size, stride=patch_size)
    patches = unfold(image)
    patches = patches.permute(0, 2, 1)
    patches = patches.reshape(-1, channel, patch_size, patch_size) # [P*B, C, W, H]
    return patches

import os
import cv2

def get_mask_dir(root_dir, prefix):
    files = os.listdir(root_dir)
    mask_list = []
    for file in files:
        path = os.path.join(root_dir, file)
        mask_list.append(path)
        if os.path.isdir(path):
            get_mask_dir(path, prefix + "    ")
    return mask_list

def get_coco_dir(root_dir, prefix):
    files = os.listdir(root_dir)
    img_list = []
    for file in files:
        path = os.path.join(root_dir, file)
        jpg = path.split('/')[-1].split('.')[0]
        img_path = os.path.join(coco_dir,jpg+'.jpg')
        img_list.append(img_path)
        if os.path.isdir(path):
            get_mask_dir(path, prefix + "    ")
    return img_list

coco_dir = '../datasets/test2017'
mask_dir = '../datasets/output_coco'

def data_split(split, ratio=0.2):
    # idx = np.arange(0, len(split))
    # np.random.shuffle(idx)
    length = int(len(split) * ratio)
    train_data = split[length:]
    test_data = split[:length]
    return train_data, test_data

class Augmentation(object) :
    def __init__(self):
        super(Augmentation, self).__init__()

        self.coco = get_coco_dir(mask_dir, "")
        self.mask = get_mask_dir(mask_dir, "")

    def __call__(self, img):
        if np.random.rand(1) >= 0.5:
            h, w = 256, 256
            index = np.random.randint(1,len(self.mask))
            mask_data = cv2.imread(self.mask[index], 0)
            coco_data = cv2.imread(self.coco[index])

            mask_data[mask_data < 254] = 1
            mask_data[mask_data == 255] = 0
            expand_mask = np.expand_dims(mask_data, axis=2)
            mask_data = np.repeat(expand_mask, 3, axis=2)

            # masked = cv2.resize(masked, dsize=(w, h), interpolation=cv2.INTER_AREA)
            coco_data = cv2.resize(coco_data, dsize=(w, h), interpolation=cv2.INTER_AREA)
            mask_data = cv2.resize(mask_data, dsize=(w, h), interpolation=cv2.INTER_AREA)

            masked = coco_data * mask_data

            rows, cols = mask_data.shape[:2]
            randint = np.random.randint(1, 360)
            # randf = float(np.round(0.5 * np.random.rand(1), 1)) # [0.5 ~ 2.0] # scale
            M = cv2.getRotationMatrix2D((cols / 2, rows / 2), randint, 1)

            mask_data = cv2.warpAffine(mask_data, M, (cols, rows))
            masked = cv2.warpAffine(masked, M, (cols, rows))

            x = np.random.randint(-150, 150)
            y = np.random.randint(-150, 150)

            T = np.float64([[1, 0, x], [0, 1, y]])
            mask_data = cv2.warpAffine(mask_data, T, (cols, rows))
            masked = cv2.warpAffine(masked, T, (cols, rows))

            if np.random.rand(1) > 0.5:
                masked = cv2.GaussianBlur(masked, (3, 3), 0)

            mask_data[mask_data == 1] = 3
            mask_data[mask_data == 0] = 1
            mask_data[mask_data == 3] = 0
            img = np.array(img)

            if len(img.shape) == 2:
                out_mask = mask_data + 1
                out_mask[out_mask == 1] = 1
                out_mask[out_mask == 2] = 0

                img[img > 0] = 1
                mask = np.bitwise_or(img, out_mask[:,:,1])
                return mask
            else :
                masked = cv2.cvtColor(masked, cv2.COLOR_BGR2RGB)
                blank = img*mask_data
                out_img = blank + masked
                return out_img
        else:
            return img


import requests
from utils import decode_encoding, encode_image
from datetime import datetime
from config import ADD_ALERT_ROUTE, GET_DATA_ROUTE

LAST_NAMES = []

def get_user_data():
    encodings = []
    names = []

    people = requests.get(GET_DATA_ROUTE).json()
    for person in people:
        if person['encoding'] and person['name']:
            encodings.append(decode_encoding(person['encoding']))
            names.append(person['name'])

    return {'encodings': encodings, 'names': names}

def alert(names, image):
    global LAST_NAMES
    should_alert = False

    for name in names:
        if name not in LAST_NAMES:
            LAST_NAMES = names
            should_alert = True
            break

    if should_alert:
        print('alerting')
        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        req = requests.post(ADD_ALERT_ROUTE, json={
            "names": names,
            "image": encode_image(image),
            "timestamp": timestamp})


"""
Stochastic layer for GPi-H BNN prior parameterisation
"""

import math
import torch
import torch.distributions as dist
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init

class HierarchicalLayer(nn.Module):
    def __init__(self, input_dim, output_dim, rbf_dim=None, prior_per='layer', fit_means=True):
        """
        Implementation of each BNN layer, when using fixed Gaussian prior over parameters.

        :param input_dim: int, number of dimensions of previous layer's output (this layer's input)
        :param output_dim: int, number of dimensions of this layer's output
        :param rbf_dim: int, width of embedding layer (number of spatial basis functions)
        :param prior_per: str, indicates either one prior per `layer`, `parameter`, or `input`
        :param fit_means: bool, specify if means are fitted as parameters (set to zero otherwise)
        """
        super().__init__()

        self.input_dim = input_dim
        self.output_dim = output_dim
        self.nonstationary = False
        self.fit_means = fit_means

        # Set dimensions of optimised parameters
        if prior_per == 'layer':
            W_shape, b_shape = 1, 1
        elif prior_per == 'parameter':
            W_shape, b_shape = (input_dim, output_dim), output_dim
        elif prior_per == 'input':
            if rbf_dim is None:
                raise Exception('For nonstationary prior (one per input), require rbf_dim (embedding layer width)')
            W_shape, b_shape = (rbf_dim, 1), (rbf_dim, 1)
            self.nonstationary = True
        else:
            raise ValueError("Accepted values: `parameter`, `layer`, or `input`")

        # Define optimised hyperparameters and require gradient (make autograd record operations)
        if self.nonstationary:
            if fit_means:
                self.W_mu_coeffs = nn.Parameter(torch.zeros(W_shape), requires_grad=True)
                self.b_mu_coeffs = nn.Parameter(torch.zeros(b_shape), requires_grad=True)
            self.W_shape_coeffs = nn.Parameter(torch.randn(W_shape), requires_grad=True)
            self.W_rate_coeffs = nn.Parameter(torch.randn(W_shape), requires_grad=True)
            self.b_shape_coeffs = nn.Parameter(torch.randn(b_shape), requires_grad=True)
            self.b_rate_coeffs = nn.Parameter(torch.randn(b_shape), requires_grad=True)
        else:
            if fit_means:
                self.W_mu = nn.Parameter(torch.zeros(W_shape), requires_grad=True)
                self.b_mu = nn.Parameter(torch.zeros(b_shape), requires_grad=True)
            self.W_shape = nn.Parameter(torch.randn(W_shape), requires_grad=True)
            self.W_rate = nn.Parameter(torch.randn(W_shape), requires_grad=True)
            self.b_shape = nn.Parameter(torch.randn(b_shape), requires_grad=True)
            self.b_rate = nn.Parameter(torch.randn(b_shape), requires_grad=True)

    def reset_parameters(self):
        """
        Reset parameters to values sampled from std normal distribution.
        """
        if self.nonstationary:
            init.normal_(self.W_shape_coeffs)
            init.normal_(self.W_rate_coeffs)
            init.normal_(self.b_shape_coeffs)
            init.normal_(self.b_rate_coeffs)
            if self.fit_means:
                init.zeros_(self.W_mu_coeffs)
                init.zeros_(self.b_mu_coeffs)
        else:
            init.normal_(self.W_shape)
            init.normal_(self.W_rate)
            init.normal_(self.b_shape)
            init.normal_(self.b_rate)
            if self.fit_means:
                init.zeros_(self.W_mu)
                init.zeros_(self.b_mu)

    def _resample_std(self, X_RBF):
        """
        Obtain std deviations from resampled inverse-gamma variances.

        :param X_RBF: torch.Tensor, embedding layer output (for nonstationary case)
        :return: tuple (torch.Tensor, torch.Tensor), weight std dev, bias std dev
        """
        # Positivity constraints
        if self.nonstationary:
            W_shape = F.softplus(X_RBF @ self.W_shape_coeffs).squeeze()
            W_rate = F.softplus(X_RBF @ self.W_rate_coeffs).squeeze()
            b_shape = F.softplus(X_RBF @ self.b_shape_coeffs).squeeze()
            b_rate = F.softplus(X_RBF @ self.b_rate_coeffs).squeeze()
        else:
            W_shape = F.softplus(self.W_shape)
            W_rate = F.softplus(self.W_rate)
            b_shape = F.softplus(self.b_shape)
            b_rate = F.softplus(self.b_rate)

        # Resample variances (sample from Gamma then invert)
        W_gamma_dist = dist.Gamma(W_shape, W_rate)
        b_gamma_dist = dist.Gamma(b_shape, b_rate)

        # Note: rsample() is reparametrised sample, which stores gradients
        inv_W_var = W_gamma_dist.rsample()
        inv_b_var = b_gamma_dist.rsample()

        # Note: self.eps added in denominator to avoid division by zero
        W_std = 1. / (torch.sqrt(inv_W_var) + 1e-10)
        b_std = 1. / (torch.sqrt(inv_b_var) + 1e-10)

        return W_std, b_std

    def forward(self, X, X_RBF=None):
        """
        Performs forward pass through layer given input data.

        :param X: torch.Tensor, size (batch_size, input_dim), input data
        :return: torch.Tensor, size (batch_size, output_dim), output data
        """
        if self.nonstationary:
            X = X.to(self.W_rho_coeffs.device)
            if X_RBF is None:
                X_RBF = X.detach().clone()
            else:
                X_RBF = X_RBF.to(self.W_rho_coeffs.device)
            W_std, b_std = self._resample_std(X_RBF)  # b_std has shape [batch_size, 1]
            W_std = W_std.unsqueeze(2)  # need shape [batch_size, 1, 1]
            if self.fit_means:
                W_mu = (X_RBF @ self.W_mu_coeffs).unsqueeze(2)  # [batch_size, 1, 1]
                b_mu = (X_RBF @ self.b_mu_coeffs)  # [batch_size, 1]
            else:
                W_mu = 0.
                b_mu = 0.
            Zw = torch.randn((1, self.input_dim, self.output_dim), device=W_std.device)
            Zb = torch.randn((1, self.output_dim), device=b_std.device)
            W = W_mu + W_std * Zw
            W = W / math.sqrt(self.input_dim)  # NTK
            b = b_mu + b_std * Zb

            # Rearrange tensors for broadcasting to work properly
            if len(X.shape) < len(W.shape):
                X = X.unsqueeze(1)
            fwd = (X @ W).squeeze() + b.squeeze()
            # X @ W is [batch_size, 1, input_dim] * [batch_size, input_dim, output_dim] = [batch_size, 1, output_dim]
            return fwd
        else:
            X = X.to(self.W_rho.device)
            if self.fit_means:
                W_mu = self.W_mu
                b_mu = self.b_mu
            else:
                W_mu = 0.
                b_mu = 0.
            W = W_mu + F.softplus(self.W_rho) * torch.randn((self.input_dim, self.output_dim), device=self.W_rho.device)
            b = b_mu + F.softplus(self.b_rho) * torch.randn(self.output_dim, device=self.b_rho.device)
            W = W / math.sqrt(self.input_dim)  # NTK
            return X @ W + b

    def sample_predict(self, X, n_samples, X_RBF=None):
        """
        Perform predictions using n_samples different sampled network parameters.

        :param X: torch.Tensor, size (batch_size, input_dim) or (n_samples, batch_size, input_dim), input data
        :param n_samples: int, number of network samples
        :param X_RBF: torch.Tensor, contains embedding layer output (RBF values in each neuron)
        :return: torch.Tensor, size (n_samples, batch_size, output_dim), output data
        """
        if self.nonstationary:
            X = X.to(self.W_rho_coeffs.device)

            # Resize input X appropriately
            if len(X.shape) == 2:
                X_RBF = X.detach().clone()
                X = X[None, :, None, :].repeat(n_samples, 1, 1, 1)
            else:
                X_RBF = X_RBF.to(self.W_rho_coeffs.device)
                X = X.unsqueeze(2)

            W_std, b_std = self._resample_std(X_RBF)
            W_std = W_std.squeeze()[None, :, None, None]
            b_std = b_std.squeeze()[None, :, None]
            if self.fit_means:
                W_mu = (X_RBF @ self.W_mu_coeffs).squeeze()[None, :, None, None]
                b_mu = (X_RBF @ self.b_mu_coeffs).squeeze()[None, :, None]
            else:
                W_mu = 0.
                b_mu = 0.
            Ws = W_mu + W_std * torch.randn((n_samples, 1, self.input_dim, self.output_dim), device=W_std.device)
            bs = b_mu + b_std * torch.randn((n_samples, 1, self.output_dim), device=b_std.device)
            Ws = Ws / math.sqrt(self.input_dim)  # NTK
            return (X @ Ws).squeeze() + bs.squeeze()
        else:
            X = X.to(self.W_rho.device)
            if self.fit_means:
                W_mu = self.W_mu
                b_mu = self.b_mu
            else:
                W_mu = 0.
                b_mu = 0.
            Ws = W_mu + F.softplus(self.W_rho) * torch.randn((n_samples, self.input_dim, self.output_dim),
                                                             device=self.W_rho.device)
            bs = b_mu + F.softplus(self.b_rho) * torch.randn((n_samples, 1, self.output_dim),
                                                             device=self.b_rho.device)
            Ws = Ws / math.sqrt(self.input_dim)  # NTK
            return X @ Ws + bs


# 완료
import re

def solution(new_id):
    answer = new_id
    # 소문자로 치환
    answer = answer.lower()
    # 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.) 조건 제외하고 공백 치환
    answer = re.sub('[^a-z0-9\-_.]', '', answer)
    # 마침표(.) 중복 문자열 마침표(.) 한 개로 치환
    answer = re.sub('\.+', '.', answer)
    # 마침표(.) 문자열 첫 번째 또는 마지막 제거
    answer = re.sub('^[.]|[.]$', '', answer)
    # 문자열이 빈 문자열이면 a를 대입
    if len(answer) == 0:
        answer = 'a'
    # 문자열의 길이가 15자 초과이면 문자열 자름
    if len(answer) > 0:
        answer = answer[:15]
    # 문자열 15자리 자른 후 마침표(.) 문자열 첫 번째 또는 마지막 제거
    answer = re.sub('^[.]|[.]$', '', answer)

    while len(answer) < 3:
        answer += answer[-1]

    return answer

test_arr = [
    "...!@BaT#*..y.abcdefghijklm",
    "z-+.^.",
    "=.=",
    "123_.def",
    "abcdefghijklmn.p",
    "iuf!@",
    "!@#!@#",
    "..3..3..3.....3..4.",
    "...........",
    "_-_-_abc_-_-",
    "..abc'29-=",
    "...,,,,===+++무무무/vb",
    ".1.112...3.124..#$%^&*().",
    ".!@#!@#BVI...USBL......VIb......abdVNVN.[]",
    "!@#!@$....!@#!@#!"]

for ar in arr_str:
    print(solution(ar))


inp = 'cqjxjnds'

def increment(s):
	x = s[-1]
	if x == 'z':
		s = s[:-1] + 'a'
		i = -1
		while True:
			i -= 1
			if i == -(len(s) + 1):
				 s = 'a' + s
				 break
			elif s[i] != 'z':
				s = s[:i] + chr(ord(s[i]) + 1) + s[i + 1:]
				break
			else:
				s = s[:i] + 'a' + s[i + 1:]
	else:
		s = s[:-1] + chr(ord(s[-1]) + 1)
	return s

def validate_1(s):
	for i in range(len(s) - 2):
		if ord(s[i]) == ord(s[i + 1]) - 1 and ord(s[i + 1]) == ord(s[i + 2]) - 1:
			return True
	return False

def validate_2(s):
	if 'i' in s:
		return False
	if 'o' in s:
		return False
	if 'l' in s:
		return False
	return True

def validate_3(s):
	pair_letters = []
	i = 0
	while i < len(s) - 1:
		if s[i] == s[i + 1]:
			if s[i] not in pair_letters:
				pair_letters.append(s[i])
			i += 1
		i += 1
	return len(pair_letters) >= 2

def validate(s):
	return validate_2(s) and validate_3(s) and validate_1(s)

s = inp
x = ""
while True:
	while not validate(s):
		s =	increment(s)
	print(s)
	x = input()
	if 'x' in x:
		break
	else:
		s = increment(s)

#print(validate_1('cqjxkkkj'))
#print(validate_2('cqjxkkkj'))
#print(validate_3('cqjxkkkj'))


#! /usr/bin/env python

import os
import waflib.Task
import waflib.TaskGen
from waflib.TaskGen import extension
from waflib.Options import options
from waf_dynamo import platform_supports_feature

waflib.Task.task_factory('proto_encode', "protoc -I ${proto_include} --encode=${proto_type} ${proto_file} < ${SRC} > ${TGT}",
                      color='PINK',
                      before='c cxx')

@extension('.pf')
def pf_file(self, node):
    obj_ext = '.pfb'
    proto_encode = self.create_task('proto_encode')
    proto_encode.set_inputs(node)
    proto_encode.set_outputs(node.change_ext(obj_ext))
    curnode = self.path
    proto_file =  curnode.find_resource(self.proto_file)
    self.env['proto_file'] = proto_file.srcpath()
    self.env['proto_include'] = os.path.dirname(proto_file.srcpath())
    self.env['proto_type'] = self.proto_type

def build(bld):
    if options.skip_build_tests:
       return

    if not platform_supports_feature(bld.env.PLATFORM, "test_ddf", None):
        print("test_ddf isn't supported on this platform", bld.env.PLATFORM)
        return

    bld(features = 'cxx cprogram ddf test',
        source = 'test_ddf.cpp test_ddf_proto.proto test_ddf_import.proto',
        use = 'TESTMAIN PROTOBUF DLIB PROFILE_NULL PTHREAD SOCKET ddf',
        web_libs = ['library_sys.js'],
        ddf_namespace = 'DUMMY',
        proto_gen_cc = True,
        proto_compile_cc = True,
        protoc_includes = '. ..',
        includes = '. .. ../ddf',
        target = 'test_ddf')


import pygame
import sys
import time
import random
from pygame.locals import *

pygame.init()
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
# timeDead is for auto die and timeGap is for respawn time
timeDead = 8
timeGap, display_width, display_height = 10, 720, 720

black = (0, 0, 0)
white = (255, 255, 255)
global_count = [0]

yy = 90
xx = 90

gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption('Space Invaders')
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
#######################################################

# store an array of images
img_array = [1, 2, 3, 4, 5]
img_array[0] = pygame.image.load("ship.png")
img_array[0] = pygame.transform.scale(img_array[0], (int(xx), int(yy)))
img_array[1] = pygame.image.load("a1.png")
img_array[1] = pygame.transform.scale(img_array[1], (int(xx), int(yy)))
img_array[2] = pygame.image.load("a2.png")
img_array[2] = pygame.transform.scale(img_array[2], (int(xx), int(yy)))
img_array[3] = pygame.image.load("m1.png")
img_array[3] = pygame.transform.scale(img_array[3], (int(xx), int(yy)))
img_array[4] = pygame.image.load("m2.png")
img_array[4] = pygame.transform.scale(img_array[4], (int(xx), int(yy)))

alien_list = []
missile1_list = []
missile2_list = []
time_start = []
prev_time = []


"""       nft approval and rejection panel for staff      """

from django.db.models import F
from rest_framework.decorators import api_view
from rest_framework import status
from rest_framework.response import Response
from django.db.models import Q
from rest_framework.pagination import PageNumberPagination

from ethereum.models import EthBalance
from nft.models import Nft
from nft.serializers import NftSerializer

# Views

@api_view(['GET'])
def nft_unapproved(request):
    try:
        nft = Nft.objects.filter(approved=False)
        paginator = PageNumberPagination()
        paginator.page_size = 10
        result_page = paginator.paginate_queryset(nft, request)
        serializer = NftSerializer(result_page, many=True)
        return paginator.get_paginated_response(serializer.data)
    except Exception as e:
        return Response({'error': f'{e}'})

@api_view(['PATCH'])
def nft_approve(request, pk):
    try:
        nft = Nft.objects.get(id=pk)
        serializer = NftSerializer(nft, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()

            # update the eth balance of an user
            """
                User get rewarded on every NFT they uplode.
                User only get eth after we approved/verified the NFT uploded by the user
            """
            if nft.approved == True:
                try:
                    eth_user = EthBalance.objects.get(user=nft.user)
                    if eth_user:
                        eth_user.eth_balance = F('eth_balance')+0.00000017
                        eth_user.save()
                        print("SUCCESSFULL UPDATED")

                #  If user uplode's their first NFT
                except Exception as e:
                    print("CREATING ETH WALLET")
                    eth_user = EthBalance.objects.create(
                            user = nft.user
                        )
                    eth_user.save()
                    print("SUCCESS")

            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(status=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        return Response({'error': f'{e}'}, status=status.HTTP_204_NO_CONTENT)

@api_view(['DELETE'])
def nft_reject(request, pk):
    try:
        nft = Nft.objects.get(id=pk)
        if request.user.is_staff == True:
            nft.delete()
            return Response(status=status.HTTP_200_OK)
        else:
            return Response(status=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        return Response({'error': f'{e}'})


'''
-Medium-

The bitwise AND of an array nums is the bitwise AND of all integers in nums.

For example, for nums = [1, 5, 3], the bitwise AND is equal to 1 & 5 & 3 = 1.
Also, for nums = [7], the bitwise AND is 7.
You are given an array of positive integers candidates. Evaluate the bitwise AND of every combination of numbers of candidates. Each number in candidates may only be used once in each combination.

Return the size of the largest combination of candidates with a bitwise AND greater than 0.

Example 1:

Input: candidates = [16,17,71,62,12,24,14]
Output: 4
Explanation: The combination [16,17,62,24] has a bitwise AND of 16 & 17 & 62 & 24 = 16 > 0.
The size of the combination is 4.
It can be shown that no combination with a size greater than 4 has a bitwise AND greater than 0.
Note that more than one combination may have the largest size.
For example, the combination [62,12,24,14] has a bitwise AND of 62 & 12 & 24 & 14 = 8 > 0.
Example 2:

Input: candidates = [8,8]
Output: 2
Explanation: The largest combination [8,8] has a bitwise AND of 8 & 8 = 8 > 0.
The size of the combination is 2, so we return 2.

Constraints:

1 <= candidates.length <= 105
1 <= candidates[i] <= 107

'''
from typing import List

class Solution:
    def largestCombination(self, candidates: List[int]) -> int:
        maxi = max(candidates)
        n = 1
        while (1<<n) < maxi:
            n += 1
        digits = [0]*(n+1)
        for i in range(n,-1,-1):
            for c in candidates:
                digits[i] += (c >> i) & 1
        # print(digits)
        return max(digits)

if __name__ =="__main__":
    print(Solution().largestCombination(candidates = [16,17,71,62,12,24,14]))
    print(Solution().largestCombination(candidates = [8,8]))