#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
----------------------------------------------------------------------------------------------------
DIRT/mu 1.1 - An automatic high throughput root hair phenotyping algorithm

The software is written in:
- python 3.9.10 (https://www.python.org)

The software depends on:
- the numpy package (http://sourceforge.net/projects/numpy/)
- the scipy package (http://www.scipy.org/SciPy)
- the graph_tool package
- the scikit image package
----------------------------------------------------------------------------------------------------
Author: Peter Pietrzyk
Department of Plant Biology
University of Georgia
Mail: peterjacek.pietrzyk@uga.edu
Web: http://www.computational-plant-science.org
----------------------------------------------------------------------------------------------------
'''

import argparse
import time
import os
import psutil
import csv

import numpy as np
from PIL import Image
import pandas as pd

import rh_io
import preprocessing
import segmentation
import candidates
import optimization
import analysis
import rh_plot
import rh_density
import graphs

def run_pipeline(args):

    time_start = time.time()
    time_intermediate = time_start

    print(args)

    meta_data = dict()

    pid = os.getpid()
    py = psutil.Process(pid)
    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))
    elapsed_time = time.time() - time_intermediate
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
    meta_data['time_0'] = elapsed_time

    # 1. Load data
    time_intermediate = time.time()

    experiment_name = os.path.splitext(os.path.basename(args.input_path))[0]
    data = rh_io.load(args.input_path)

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))
    elapsed_time = time.time() - time_intermediate
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
    meta_data['time_load'] = elapsed_time

    # 2. Prepocess and put into correct format
    time_intermediate = time.time()
    prep = preprocessing.Preprocessing(max_distance=args.max_dist, # max distance in pixels
                                       id_root=args.id_root,
                                       id_background=args.id_background,
                                       id_roothair=args.id_roothair,
                                       is_prune=args.is_prune,
                                       is_close_gaps=args.is_close_gaps)

    ma, ma_dist, dist_to_root, data = prep.run(data)

    area_roothair = np.count_nonzero(data==args.id_roothair)
    area_root = np.count_nonzero(data==args.id_root)
    area_background = np.count_nonzero(data==args.id_background)

    if args.print_all is not None:
        prep.out(args.print_all)

    del prep

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))
    elapsed_time = time.time() - time_intermediate
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
    meta_data['time_preprocess'] = elapsed_time

    # 3. CREATE SEGMENTS OF MEDIAL AXIS
    time_intermediate = time.time()
    rh_segm = segmentation.Segmentation(ma,ma_dist)
    rh_segm.classifiyTips(dist_to_root, args.thresh_dist_to_root)       # Classifiy tips into root or tip

    # Initialize object for branching graph.
    # Create nodes in graph from dict of segments in medial axis.
    # Connect nodes. Results in all neighborhood information of medial axis
    g = segmentation.Graph()
    graph = g.create(rh_segm.segments)
    vertices = g.vertices

    # TODO:QUICK FIX FOR WRONG SEGMENTTYPES IN bg.segmentType
    for s in rh_segm.segments.values():
        v = vertices[s.label]
        if v.out_degree() == 1:
            s.type = 1
    for v in graph.vertices():
        if v.out_degree() == 1:
            rh_segm.segmentType[graph.vertex_properties['label'][v]] = 1

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))
    elapsed_time = time.time() - time_intermediate
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
    meta_data['time_segments'] = elapsed_time
    meta_data['n_tips'] = len(np.where(np.array(list(rh_segm.segmentType.values()))==1)[0])
    meta_data['n_junctions'] = len(np.where(np.array(list(rh_segm.segmentType.values()))>2)[0])

    # 4. Get candidates
    time_intermediate = time.time()
    rh_candidates, dummies = candidates.pipeline(graph=graph, segments=rh_segm.segments)

    all_candidates = [item for sublist in rh_candidates.values() for item in sublist] #flatten
    all_dummies = [item for sublist in dummies.values() for item in sublist] #flatten

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))
    meta_data['n_clusters'] = len(rh_candidates)
    meta_data['n_candidates'] = len(all_candidates)

    # 4.1 Gather data for dummies
    print('Gathering data for dummies...')
    dummy_lengths = []
    dummy_min_dist = []
    dummy_max_dist = []
    for path in all_dummies:
        d = candidates.Candidate(path, rh_segm.segments)
        d.fitCurve()
        d_min, d_max = d.connectivity()
        dummy_lengths.append(d.length())
        dummy_min_dist.append(d_min)
        dummy_max_dist.append(d_max)
    # Dummy values
    dummy_lengths = np.array(dummy_lengths)
    dummy_min_dist = np.array(dummy_min_dist)
    dummy_max_dist = np.array(dummy_lengths)

    if sum(dummy_min_dist<2) > 0: # Use dummies attached to root; take mean of their max distance to root
        normDistanceHigh = np.mean(dummy_max_dist[dummy_min_dist<2])
    else:                         # If no dummies are attached to root, use mean of dummy lengths
        normDistanceHigh = np.mean(dummy_lengths)
    normDistanceLow = 1.0  # root hair is always at least one pixel away from root

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))

    # 4.2 Gather data for candidates

    n_candidates = len(all_candidates)
    print('Gathering data for candidates...')
    print(" - "+str(n_candidates)+" candidate(s)")

    # Reference curvature and strain per segment
    # Does not store candidate inform
    if args.measure == 'strain_energy':
        ref_segment_strain = candidates.ReferenceValues('strain', args.use_ref_tips)
    ref_segment_curvature = candidates.ReferenceValues('curvature', args.use_ref_tips)

    segment_ids = []
    total_curvature = []
    for i,path in enumerate(all_candidates):

        if i%10000 == 0:
            print(' - Candidate '+str(i))

        c = candidates.Candidate(path, rh_segm.segments)
        c.fitCurve(is_dummy=False)
        if args.measure == 'strain_energy':
            ref_segment_strain.add(c)
        ref_segment_curvature.add(c)
        segment_ids.append(c.segment_ids)               # Append start-end index of segments in curve
        total_curvature.append(c.totalcurvature())      # Append total curvature of curve

    # Calculate reference curvature for each candidate and extract
    # only candidates with total excess curvature < 180 degrees
    # and maximum excess curvature per segment < 45 degrees
    min_reference_curvature = []
    max_ref_curve_per_seg = []
    for i,path in enumerate(all_candidates):
        min_ref_value,_ = ref_segment_curvature.calc(path, rh_segm.segments, segment_ids[i])
        min_reference_curvature.append(min_ref_value)
        max_diff_value = ref_segment_curvature.calc_max_difference(path, rh_segm.segments, ref_segment_curvature.curvatures_per_segment[i])
        max_ref_curve_per_seg.append(max_diff_value)
    candidate_filter = np.logical_and(np.array(max_ref_curve_per_seg) <= 0.25*np.pi, np.array(total_curvature) - np.array(min_reference_curvature) <= np.pi)
    good_candidates = np.array(all_candidates)[candidate_filter] #[all_candidates[i] for i in candidate_filter]
    print('Keeping '+str(len(good_candidates))+' of '+str(n_candidates)+' candidates')

    # Normalization values for curvature metric
    n_segments = np.array([(len(p)-1)/2 for p in all_candidates])
    ids = np.array(total_curvature) - np.array(min_reference_curvature) > 0
    normCurveHigh = np.mean((np.array(total_curvature)[ids] - np.array(min_reference_curvature)[ids]))# / n_segments[ids])
    normCurveLow = 0.0

    # Get information for good candidates
    lines = []
    segment_ids = []
    curve_measure = []
    min_distance = []
    max_distance = []
    min_reference_curve = []

    for i,path in enumerate(good_candidates):

        if i%10000 == 0:
            print(' - Candidate '+str(i))

        c = candidates.Candidate(path, rh_segm.segments)
        c.fitCurve(is_dummy=False)

        d_min, d_max = c.connectivity()
        lines.append(np.vstack((c.curve.x, c.curve.y))) # Append curve
        segment_ids.append(c.segment_ids)               # Append start-end index of segments in curve
        if args.measure == 'strain_energy':
            curve_measure.append(c.strainenergy())          # Append strain energy of curve
            min_ref_value,_ = ref_segment_strain.calc(path, rh_segm.segments, c.segment_ids)
        else:
            curve_measure.append(c.totalcurvature())          # Append strain energy of curve
            min_ref_value,_ = ref_segment_curvature.calc(path, rh_segm.segments, c.segment_ids)
        min_reference_curve.append(min_ref_value)
        min_distance.append(d_min)                      # Append min distance to root
        max_distance.append(d_max)                      # Append max distance to root

    curve_measure = np.array(curve_measure)
    min_distance = np.array(min_distance)
    max_distance = np.array(max_distance)
    min_reference_curve = np.array(min_reference_curve)

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))

    # Create object to hold all candidate/dummy information to calculate cost
    cand_info = optimization.CandidateInformation()
    cand_info.paths = good_candidates
    cand_info.n_segments = np.array([(len(p)-1)/2 for p in good_candidates])

    # Set information from dummies
    cand_info.dummy_lengths = dummy_lengths

    # Set information from candidates
    cand_info.excess_strain = curve_measure-min_reference_curve
    cand_info.min_distance = min_distance
    cand_info.max_distance = max_distance

    # Minimum distance of segments to root
    cand_info.minDistToEdge = {key: val.minDistToEdge for key, val in rh_segm.segments.items()}

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))

    # 4.3 Conflicts: For each roothair get a list of conflicting roothairs
    print(" - Computing conflicts...")
    conflicts = candidates.Conflicts(good_candidates, lines, segment_ids, rh_segm.segments, data)
    conflicts_list, merge_list , adj_list = conflicts.create()

    # 4.4 Compute offset for merged candidates and add merged curves to best curvatures per segment (ref_segment_strain or ref_segment_curvature)
    offset_dict = {}

    for cand_i in range(len(merge_list)):

        if cand_i%1000 == 0:
                print(' - Merging candidate '+str(cand_i))

        for cand_j in merge_list[cand_i]:
            if cand_i>=cand_j:
                continue

            # Construct path of connected candidates
            g = graphs.Candidate_Graph(cand_info.paths[cand_i])
            g.merge(graphs.Candidate_Graph(cand_info.paths[cand_j]))
            merged_path = g.get_path()
            c = candidates.Candidate(merged_path, rh_segm.segments)
            c.fitCurve(is_dummy=False)

            if args.measure == 'strain_energy':
                merged_curve_measure = c.strainenergy()                # Calculate strain energy of curve
                ref_segment_strain.add(c)
            else:
                merged_curve_measure = c.totalcurvature()              # Calculate total curvature of curve
                ref_segment_curvature.add(c)

            curvature_offset =  merged_curve_measure - curve_measure[cand_i] - curve_measure[cand_j]
            offset_dict[(cand_i,cand_j)] = curvature_offset

    # Need to recalculate reference curvature for good candidates and for merged candidates
    # Recaluclate reference curvature for good candidates
    min_reference_curve = []
    for i,path in enumerate(good_candidates):
        if i%10000 == 0:
            print(' - Candidate '+str(i))
        c = candidates.Candidate(path, rh_segm.segments)
        if args.measure == 'strain_energy':
            min_ref_value,_ = ref_segment_strain.calc(path, rh_segm.segments, c.segment_ids)
        else:
            min_ref_value,_ = ref_segment_curvature.calc(path, rh_segm.segments, c.segment_ids)
        min_reference_curve.append(min_ref_value)
    min_reference_curve = np.array(min_reference_curve)
    cand_info.min_reference_strain = min_reference_curve

    # Recaluclate information for merged candidates
    for cand_i in range(len(merge_list)):
        if cand_i%1000 == 0:
                print(' - Merging candidate '+str(cand_i))
        for cand_j in merge_list[cand_i]:
            if cand_i>=cand_j:
                continue

            # Construct path of connected candidates
            g = graphs.Candidate_Graph(cand_info.paths[cand_i])
            g.merge(graphs.Candidate_Graph(cand_info.paths[cand_j]))
            merged_path = g.get_path()
            c = candidates.Candidate(merged_path, rh_segm.segments)

            if args.measure == 'strain_energy':
                min_ref_value,_ = ref_segment_strain.calc(merged_path, rh_segm.segments, c.segment_ids)
            else:
                min_ref_value,_ = ref_segment_curvature.calc(merged_path, rh_segm.segments, c.segment_ids)
            ref_value_offset = min_reference_curve[cand_i] + min_reference_curve[cand_j] - min_ref_value
            offset_dict[(cand_i,cand_j)] += ref_value_offset

    """
    curves = []
    for i,path in enumerate(good_candidates):
        c = candidates.Candidate(path, rh_segm.segments)
        c.fitCurve(is_dummy=False)
        curves.append(c.curve)
    """
    #rh_plot.plot_colored_candidates(curves, np.array(curve_measure) - np.array(min_reference_curve), data, "/mnt/c/Projects/Roothair/Images/temp/")
    #rh_plot.plot_colored_candidates(curves_array, excess_curvature_array, data, "/mnt/c/Projects/Roothair/Images/temp/merged_")

    # For each roothair get a list of conflicting dummies
    print(" - Computing conflicts with dummies...")
    rh_dummy_conflicts = candidates.DummyConflicts(good_candidates, all_dummies)
    rh_dummy_conflicts_list = rh_dummy_conflicts.create()

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))

    n_candidates = len(good_candidates)   # number of candidates
    print(" - "+str(n_candidates)+" candidate(s)")

    elapsed_time = time.time() - time_intermediate
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
    meta_data['time_candidates'] = elapsed_time

    # 5. Optimize
    time_intermediate = time.time()

    # Set weights for optimization and initilize cost object
    weights = [args.w_curve, args.w_len, args.w_mind]
    normValuesLow = [normCurveLow, 0.0, normDistanceLow]
    normValuesHigh = [normCurveHigh, 1.0, normDistanceHigh]
    costCalculator = optimization.Cost(measure=args.measure , cost_type=args.cost_type, weights=weights,normValuesLow=normValuesLow, normValuesHigh=normValuesHigh)
    optimizer = optimization.Optimize(cost=costCalculator, nIterations=args.n_levels, n_repeats=args.n_repeats)

    # Run optimization
    roothair_paths, solution_summary, sa_parameters = optimizer.run(cand_info, conflicts_list, merge_list, adj_list, rh_dummy_conflicts_list, offset_dict)
    solution_roothairs = [candidates.Candidate(path, rh_segm.segments) for path in roothair_paths]

    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB...I think
    print('memory use:', round(memoryUse,4))
    elapsed_time = time.time() - time_intermediate
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))
    meta_data['time_optimize'] = elapsed_time

    # 6. Post Processing
    time_intermediate = time.time()
    post = analysis.PostProcess(validity=False, thresh_l2d_ratio=1.2, thresh_connectivity=0.8) # postprocessing and analysis
    inliers, outliers = post.run(solution_roothairs, get_longest=False, return_outliers=True)
    elapsed_time = time.time() - time_intermediate
    meta_data['time_post'] = elapsed_time

    # **************
    # 7. Analysis
    # **************
    time_intermediate = time.time()

    # Compute root hair density and root class
    summary, root_classes, root_positions, edge_info = rh_density.computeDensity(data, [rh.curve for rh in inliers], rootIdx=args.id_root, pixel_size=args.pixel_size)

    # Root hair measurements
    results = analysis.Results()
    table_roothairs = results.get(inliers, args.pixel_size, root_classes=root_classes, root_positions=root_positions)
    table_outliers = results.get(outliers, args.pixel_size, root_classes=['None']*len(outliers), root_positions=['None']*len(outliers))

    summary['Area Roothair (pxl)'] = area_roothair
    summary['Area Root (pxl)'] = area_root
    summary['Area Background (pxl)'] = area_background

    summary['Ratio Completeness'] = solution_summary['SA_resultRatioComplete']
    summary['Length Inliers (mu)'] = sum([c.length() for c in inliers]) * args.pixel_size
    summary['Length Outliers (mu)'] = sum([c.length() for c in outliers]) * args.pixel_size

    elapsed_time = time.time() - time_intermediate
    meta_data['time_analysis'] = elapsed_time

    # *****************
    # 8. Save result
    # *****************
    time_intermediate = time.time()

    rh_io.save_table(table_roothairs, os.path.join(args.output_path, experiment_name+'_roothairs.csv'))
    col_order = sorted(summary.keys(), key=lambda x:x.lower())
    table = pd.DataFrame(summary, index=[experiment_name])
    table = table[col_order]
    rh_io.save_table(table, os.path.join(args.output_path, experiment_name+'_summary.csv'))

    if args.print_all:
        # results.out(data, inliers, os.path.join(args.output_path, experiment_name+'_roothairs.pkl'))
        # results.out(data, outliers, os.path.join(args.output_path, experiment_name+'_outliers.pkl'))
        im = Image.fromarray(data)
        im.save(os.path.join(args.output_path, experiment_name+"_classes.tiff"),compression='tiff_lzw')
        rh_io.save_table(table_outliers,  os.path.join(args.output_path, experiment_name+'_outliers.csv'))

    elapsed_time = time.time() - time_intermediate
    meta_data['time_save'] = elapsed_time
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))

    # ******************
    # 9. Plot results
    # ******************
    time_intermediate = time.time()
    if args.print_all:
        rh_plot.plot_results([c.curve for c in inliers], data, os.path.join(args.output_path, experiment_name+'_roothairs.png'))
        rh_plot.plot_results([c.curve for c in outliers], data, os.path.join(args.output_path, experiment_name+'_outliers.png'))
        if args.id_root in data: # Only if root exists
            rh_density.plotDensity([rh.curve for rh in inliers], data, edge_info['closestSegments'],
                                    edge_info['edge_classes'], edge_info['edge_segments'],
                                    edge_info['edge_position'],  os.path.join(args.output_path, experiment_name+'_density.png'))

    elapsed_time = time.time() - time_intermediate
    meta_data['time_plot'] = elapsed_time
    print('Elapsed time: ' + time.strftime("%H:%M:%S", time.gmtime(elapsed_time)))

    # *************************
    # 10. Collect Meta Data
    # *************************
    meta_data['n_roothairs'] = len(inliers)
    meta_data['n_outliers'] = len(outliers)

    time_total = time.time() - time_start
    meta_data['time_total'] = time_total
    print('Total time: ' + time.strftime("%H:%M:%S", time.gmtime(time_total)))

    if args.print_all:
        with open(os.path.join(args.output_path, experiment_name+'_meta.csv'), 'w') as f:
            writer = csv.writer(f)
            sortednames=sorted(meta_data.keys(), key=lambda x:x.lower())
            for name in sortednames:
                writer.writerow((name,meta_data[name]))
            sortednames=sorted(sa_parameters.keys(), key=lambda x:x.lower())
            for name in sortednames:
                writer.writerow((name,sa_parameters[name]))
            sortednames=sorted(solution_summary.keys(), key=lambda x:x.lower())
            for name in sortednames:
                writer.writerow((name,solution_summary[name]))

    print(" ")
    print("**************************************************")
    print("                    Finished!                     ")
    print("**************************************************")

def main():
    parser=argparse.ArgumentParser(description="Extracts and measures root hairs from classified image.")

    '''
    Input/output parameters
    '''
    parser.add_argument("-i","--in", dest="input_path", type=str, required=False,
                        default='TAKFA1-c1-1_Classes.tiff',
                        help="tiff input file")

    parser.add_argument("-o","--out", dest="output_path", type=str, required=False,
                        default='samples/', help="csv output filename")

    parser.add_argument("-p", "--print", dest="print_all", required=False,
                        default=False, action='store_true', help="Select to output other data")

    parser.add_argument("--pixel_size", dest="pixel_size", type=float, required=False,
                        default=1, help="pixel size in microns per pixel")           # Default is 1

    '''
    Indices for root. background, root hair
    '''
    parser.add_argument("--id_root", dest="id_root", type=int, required=False,
                        default=3, help="id of root")           # Default is 1

    parser.add_argument("--id_background", dest="id_background", type=int, required=False,
                        default=2, help="id of background")     # Default is 2

    parser.add_argument("--id_roothair", dest="id_roothair", type=int, required=False,
                        default=1, help="id of root hairs")     # Default is 3

    '''
    Preprocessing parameters
    '''
    parser.add_argument("--max_dist", dest="max_dist", type=float, required=False,
                        default=10, help="Maximum distance of root hair clusters to root in pixels.")

    parser.add_argument("--thresh_d2r", dest="thresh_dist_to_root", type=int, required=False,
                        default=10, help="Minimum distance of root hair to root")

    parser.add_argument("--prune", dest="is_prune", type=bool, required=False,
                        default=True, help="Preprocessing step: Prune medial axis")

    parser.add_argument("--bin_op", dest="is_close_gaps", type=bool, required=False,
                        default=True, help="Preprocessing step: Binary opening/closing")

    '''
    Optimization parameters
    '''
    parser.add_argument("--measure", dest="measure", type=str, required=False,
                        default='total_curvature', choices=['strain_energy', 'total_curvature'],
                        help="Type of curvature measure")

    parser.add_argument("--cost_type", dest="cost_type", type=str, required=False,
                        default='rms', choices=['mean', 'exp', 'rms', 'pow3', 'pow4', 'geom'],
                        help="Way to summarize optimization objectives.")

    parser.add_argument("--n_levels", dest="n_levels", type=int, required=False,
                        default=1000, help="Minimum number of iteration levels for optimization.")

    parser.add_argument("--n_repeats", dest="n_repeats", type=int, required=False,
                        default=1, help="Number of times simulated annealing is repeated.")

    parser.add_argument('--use_ref_tips', dest='use_ref_tips', action='store_true',
                            help="Treat tips separately from non-tips for referance values.")
    parser.add_argument('--no_ref_tips', dest='use_ref_tips', action='store_false',
                            help="Do not treat tips separately from non-tips for referance values.")
    parser.set_defaults(use_ref_tips=True)

    '''
    Weights
    '''
    parser.add_argument("--w_curve", dest="w_curve", type=float, required=False,
                        default=1.0, help="Weight for curvature optimzation.")
    parser.add_argument("--w_len", dest="w_len", type=float, required=False,
                        default=1.0, help="Weight for length optimzation.")
    parser.add_argument("--w_mind", dest="w_mind", type=float, required=False,
                        default=1.0, help="Weight for minimum distance to root optimzation.")

    parser.set_defaults(func=run_pipeline)
    args=parser.parse_args()
    parser.print_help()
    parser.print_usage()
    args.func(args)

if __name__=="__main__":
    main()


import matplotlib.pyplot as plt
from matplotlib import rcParams
import matplotlib.ticker as mticker
import seaborn as sns
rcParams.update({'figure.autolayout': True})
sns.set_style('ticks')
import numpy as np

def plot_map(matrix, terms=None, similarities=None, show=False, vertical_grid=True, save_name=None, fig_size=(30,20)):
  '''
  Plot of a matrix of documents (matrix lines) vs terms' occurrences (matrix columns).

  Parameters
  ----------
  matrix : numpy array of shape (number_of_docs, number_of_terms)
    The occurrence matrix. The lines stand for the documents and the columns for the terms.
    The matrix must be binary: a one value indicates the occurrence of the term in the document,
    and a zero indicates the opposite.
  terms : a list of strings
    The string terms. Attention: the indexes in this list do not match the matrix columns' indexes.
    It must be provided if the similarities parameter is also provided.
  similarities : a numpy structured array of shape (number_of_terms) whose elements contain the
  term_index and the similarity fields. The indexes of this array match the columns of the matrix parameter.
    It stores the term indexes (i.e., the index of the term in the terms parameter) and the
    similarity value of the term. Use the the term_index field to get the term string from the
    terms parameter.
  show : boolean
    Indicates if the plot must be shown on the screen.
  vertical_grid : boolean
    Indicates if the vertical grid lines are visible.
  save_name : string
    The name of the file to save the plot into. If None, the plot is not saved.
  '''
  _, m_axis = plt.subplots(1, figsize=fig_size)
  m_axis.imshow(matrix, aspect='auto', interpolation=None, vmin=0.0, vmax=1.0)
  m_axis.set_yticks(np.arange(0, matrix.shape[0]))
  m_axis.set_yticks(np.arange(-.5, matrix.shape[0], 0.5), minor=True)
  if vertical_grid or similarities:
    m_axis.set_xticks(np.arange(0, matrix.shape[1]))
  if vertical_grid:
    m_axis.set_xticks(np.arange(-.5, matrix.shape[1], 0.5), minor=True)
  if similarities is not None:
    labels = []
    for s in similarities:
      t_idx = s['term_index']
      sim = s['similarity']
      labels.append(f'{terms[t_idx]} ({sim:.2f})')
    m_axis.set_xticklabels(labels, rotation=90)
  m_axis.grid(which='minor', color='gray', linestyle='-', linewidth=1)
  if show:
    plt.show()
  if save_name:
    plt.savefig(save_name, bbox_inches='tight')


# Author: Alexey Kleymenov
# Original idea: Piotr Krysiuk <piotr_krysiuk@symantec.com>

import os
import pefile
import zlib

IS_WIDE = 0x1
IS_UPPER = 0x2
IS_LOWER = 0x4
WITH_NULL = 0x8

# ################# OPTIONS ###################
PATH_TO_DLLS = 'c:\\dlls'

DLL_ENUM_NAME = 'dll_hashes'
ENUM_VALUE_SUFFIX = '_checksum'

API_FORMAT = WITH_NULL
DLL_FORMAT = WITH_NULL | IS_WIDE | IS_UPPER
DLL_WITH_FILE_EXTENSION = True
ADD_DLL_CHECKSUM = True

IS_CRC32 = False
IS_ROR = True
IS_ROL = False
SHIFT_VALUE = 0x0D
# #############################################

def format_value(name, format):
	if format & WITH_NULL:
		name += '\x00'
	if format & IS_UPPER:
		name = name.upper()
	elif format & IS_LOWER:
		name = name.lower()
	if format & IS_WIDE:
		try:
			name = name.encode('utf-16le').decode('latin-1')
		except Exception as e:
			Message('%s - %s\n' % (name, str(e)))
	return name

def calculate_checksum(name):
	value = 0
	if IS_CRC32:
		value = zlib.crc32(name) & 0xFFFFFFFF
	else:
		for symbol in name:
			if IS_ROR:
				value = ((value >> SHIFT_VALUE) | (value << (0x20 - SHIFT_VALUE))) & 0xFFFFFFFF
			elif IS_ROL:
				value = ((value << SHIFT_VALUE) | (value >> (0x20 - SHIFT_VALUE))) & 0xFFFFFFFF
			else:
				raise Exception('Unsupported checksum algorithm')
			value += ord(symbol) & 0xFFFFFFFF
	return value

def build_mappings(dll_filepath):
	dll = pefile.PE(dll_filepath, fast_load=True)
	dll.parse_data_directories(directories=[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT']])
	result = {}
	if hasattr(dll, 'DIRECTORY_ENTRY_EXPORT'):
		dll_name = os.path.basename(dll_filepath)
		if DLL_WITH_FILE_EXTENSION is False:
			dll_name = ".".join(dll_name.split(".")[:-1])
		dll_checksum = calculate_checksum(format_value(dll_name, DLL_FORMAT))
		Message('%s - %x\n' % (dll_name, dll_checksum))
		dll_name = dll_name.replace('.', '_')
		result[dll_checksum] = {'dll_name': dll_name}

		export_directory = dll.DIRECTORY_ENTRY_EXPORT
		for symbol in export_directory.symbols:
			if symbol.name is not None:
				api_name = symbol.name.decode('latin-1')
				api_checksum = calculate_checksum(format_value(api_name, API_FORMAT))
				if ADD_DLL_CHECKSUM:
					api_checksum = (api_checksum + dll_checksum) & 0xFFFFFFFF
				result[api_checksum] = {'dll_name': dll_name, 'api_name': api_name}
# 				Message('%s - %x\n' % (api_name, api_checksum))
	return result

def parse_dlls(path_to_dlls):
	list_dlls = os.listdir(path_to_dlls)
	mappings = {}
	for dll_filename in list_dlls:
		Message('Processing %s\n' % dll_filename)
		mappings.update(build_mappings(os.path.join(path_to_dlls, dll_filename)))
	return mappings

def create_enums(mappings):
	# allows digesting mappings from json
	if type(next(iter(mappings))) != int:
		mappings = {int(key): value for key, value in mappings.iteritems()}
	created_enums = set()
	dll_enum = GetEnum(DLL_ENUM_NAME)
	if dll_enum == 0xFFFFFFFF:
		dll_enum = AddEnum(GetEnumQty(), DLL_ENUM_NAME, FF_DWRD | FF_0NUMH)
	for checksum, metadata in mappings.iteritems():
		if 'api_name' in metadata:
			if metadata['dll_name'] in created_enums:
				enum = GetEnum(metadata['dll_name'].encode('latin-1'))
			else:
				enum = AddEnum(GetEnumQty(), metadata['dll_name'].encode('latin-1'), FF_DWRD | FF_0NUMH)
				created_enums.add(metadata['dll_name'])
			ret_code = AddConst(enum, (metadata['api_name'] + ENUM_VALUE_SUFFIX).encode('latin-1'), checksum)
			if ret_code:
				Message('Warning: %s - %x\n' % (metadata['api_name'], ret_code))
		else:
			ret_code = AddConst(dll_enum, (metadata['dll_name'] + ENUM_VALUE_SUFFIX).encode('latin-1'), checksum)
			if ret_code:
				Message('Warning: %s - %x\n' % (metadata['dll_name'], ret_code))
	return mappings

def bruteforce_enum_values(enums):
	for head in Heads():
		flags = GetFlags(head)
		if isCode(flags):
			if GetOpType(head, 0) == 5:
				operand_num = 0
			elif GetOpType(head, 1) == 5:
				operand_num = 1
			else:
				continue
			value = GetOperandValue(head, operand_num)
			if value in enums:
				if 'api_name' in enums[value]:
					Message('%x - found match: %s\n' % (head, enums[value]['api_name']))
					OpEnumEx(head, operand_num, GetEnum(enums[value]['dll_name'].encode('latin-1')), 0)
				else:
					Message('%x - found match: %s\n' % (head, enums[value]['dll_name']))
					OpEnumEx(head, operand_num, GetEnum(DLL_ENUM_NAME), 0)

bruteforce_enum_values(create_enums(parse_dlls(PATH_TO_DLLS)))


import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
import time

class HtmlPage():
    """
    Class containing basic html page layout.
    """
    header = '<title>%s v2</title>'

    footer = ''

class SpeciesInfo():
    """
    Class containing species info lookup tables.
    """
    common_names = ['Alder',
                    'Aspen',
                    'Starthistle',
                    'Juniper',
                    'Serviceberry',
                    'Sagebrush',
                    'Sumac',
                    'Wildrose',
                    'Fern',
                    'Maple',
                    'Elderberry',
                    'Pine',
                    'Cottonwood',
                    'Willow']

    latin_binomials = [ 'Alnus tenuifolia',
                        'Populus tremuloides',
                        'Centaurea solstitialis',
                        'Juniperus scopulorum',
                        'Amelanchier utahensis',
                        'Artemesia tridentata',
                        'Rhus glabra',
                        'Rosa woodsii',
                        'Pteridium aquilinum',
                        'Acer grandidentatum',
                        'Sambucus nigra',
                        'Pinus ponderosa',
                        'Populus trichocarpa',
                        'Salix fragilis']

    #These levels all need to correspond to the values used in the Unity module.
    #The values shown here are only used for display purposes.
    water_levels = ['High',
                    'Normal',
                    'Normal',
                    'Low',
                    'Normal',
                    'Low',
                    'Normal',
                    'Normal',
                    'High',
                    'Normal',
                    'Normal',
                    'Low',
                    'Normal',
                    'High']

    light_levels = ['Normal',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Low',
                    'High',
                    'High',
                    'Low',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Normal',
                    'Normal']

    temperature_levels = ['Low',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Low',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal',
                          'Normal']

    elevations = [   'Low',
                    'Mid',
                    'Mid',
                    'Mid',
                    'High',
                    'Mid',
                    'Mid',
                    'Mid',
                    'Mid',
                    'Mid',
                    'High',
                    'Mid',
                    'Low',
                    'Mid']

    lifespans = [   'Long (25 years)',
                    'Long (25 years)',
                    'Very Short (3 years)',
                    'Very Long (100 years)',
                    'Short (10 years)',
                    'Very Long (100 years)',
                    'Short (10 years)',
                    'Very Short (3 years)',
                    'Very Short (3 years)',
                    'Long (25 years)',
                    'Short (10 years)',
                    'Very Long (100 years)',
                    'Long (25 years)',
                    'Short (10 years)']

    colonizing_levels = [   'Medium',
                            'Medium',
                            'High',
                            'Low',
                            'Medium',
                            'Low',
                            'Medium',
                            'High',
                            'High',
                            'Medium',
                            'Medium',
                            'Low',
                            'Medium',
                            'Medium']

    replaces_lists = [  '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '',
                        '']

    replaced_by_lists = ['',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '',
                         '']

    other_notes = [ '',
                    '',
                    'Invasive species',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '',
                    '']

class MeadowRecordObject(db.Model):
    """
    Record class representing all the parameters to run a community simulation.
    """
    # Timestamp id for this record
    id = db.StringProperty()

    # CSV integers represeting the Unity3D Tree types for each of the (up to) 5 species in the community
    plant_types = db.StringProperty()

    #Water, light, temperature, and disturbance levels
    water_level = db.IntegerProperty()
    light_level = db.IntegerProperty()
    temperature_level = db.IntegerProperty()
    disturbance_level = db.IntegerProperty()

    #50x50 matrix representing the starting values for each position in the matrix
    #(R=random, N=permanent disturbance, 0=gap, 1-5=plant types)
    starting_matrix = db.TextProperty()

class ParametersFormPageOne(webapp2.RequestHandler):
    """
    First page of the two page community parameters form.
    Accessed by the user by url or hyperlink. Controls environment
    parameters, disturbance level, and species choices.
    """
    def get(self):
        page = HtmlPage()
        info = SpeciesInfo()
        self.response.out.write(page.header % 'Simulation parameters form')
        self.response.out.write(self.form_environment_settings)
        self.response.out.write(self.form_plant_choices_header)
        for i in range(1,6):
            default_selection = ['', '', '', '', '']
            default_selection[i - 1] = 'selected="selected"'
            link_option = ''
            none_option = self.form_none_option
            if (i == 1):
                link_option = self.form_plant_examples_link
                none_option = ''
            self.response.out.write(self.form_plant_choices %
                                    (i, i, none_option, info.common_names[0], default_selection[0],
                                     info.common_names[1], info.common_names[2], info.common_names[3],
                                     info.common_names[4], info.common_names[5], info.common_names[6],
                                     info.common_names[7], default_selection[1], info.common_names[8],
                                     default_selection[2], info.common_names[9], info.common_names[10],
                                     default_selection[3], info.common_names[11], info.common_names[12],
                                     default_selection[4], info.common_names[13], link_option))
        self.response.out.write(self.form_starting_distribution)
        self.response.out.write(self.form_submit_button)
        self.response.out.write(page.footer)

    form_environment_settings = """
        <form enctype="multipart/form-data" action="/parametersform2" method="post">

 Environment Settings:

                Water/moisture/precipitation level:
                <select name="water_level">
                    <option value="4">Highest</option>
                    <option value="3">Higher</option>
                    <option value="2" selected="selected">Normal</option>
                    <option value="1">Lower</option>
                    <option value="0">Lowest</option>
                </select>

                Light level:
                <select name="light_level">
                    <option value="4">Highest</option>
                    <option value="3">Higher</option>
                    <option value="2" selected="selected">Normal</option>
                    <option value="1">Lower</option>
                    <option value="0">Lowest</option>
                </select>

                Temperature level:
                <select name="temperature_level">
                    <option value="4">Highest</option>
                    <option value="3">Higher</option>
                    <option value="2" selected="selected">Normal</option>
                    <option value="1">Lower</option>
                    <option value="0">Lowest</option>
                </select>

                Ongoing disturbance level:
                <select name="disturbance_level">
                    <option value= "0">None</option>
                    <option value= "1">Very Low</option>
                    <option value= "2">Low</option>
                    <option value= "3">High</option>
                    <option value= "4">Very High</option>
                </select>

    """

    form_plant_choices_header = """

Species Settings:

            Select plant species to include in the community.

        """

    form_plant_choices = """

            Species %s
            <select name="plant_code_%s">
                %s
                <option value = "0">%s</option>
                <option value = "1" %s>%s</option>
                <option value = "2">%s</option>
                <option value = "3">%s</option>
                <option value = "4">%s</option>
                <option value = "5">%s</option>
                <option value = "6">%s</option>
                <option value = "7">%s</option>
                <option value = "8" %s>%s</option>
                <option value = "9" %s>%s</option>
                <option value = "10">%s</option>
                <option value = "11" %s>%s</option>
                <option value = "12">%s</option>
                <option value = "13" %s>%s</option>
            </select>
            %s
        """

    form_none_option = '<option value = "-1">**None**</option>'

    form_plant_examples_link = 'View examples'

    form_starting_distribution = """

            Select how the plants should be distributed in Year 0.

            <select name="starting_distribution">
                <option value = "0">Place plants randomly</option>
                <option value = "1">Let me choose</option>
            </select>

        """

    form_submit_button = '<input type="submit" name="next" value="Continue..."></form>'

class ParametersFormPageTwo(webapp2.RequestHandler):
    """
    Second page of the two page community parameters form.
    Controls the starting community.
    """
    def post(self):
        is_nonrandom_distribution = self.request.get('starting_distribution')
        if (is_nonrandom_distribution == '1'):
            #They want to choose the starting plant distribution
            self.response.out.write(self.header % ('Simulation parameters form', self.javascript))
            self.draw_form()
            self.response.out.write(self.footer)
        else:
            #They want a random distribution
            #This is poorly coded.  I'm just duplicating most of the StoreNewParametersClass!
            page = HtmlPage()
            self.response.out.write(page.header % 'Simulation parameters form')
            self.id = str(int(time.time() * 1000.0))
            self.store_record()
            self.response.out.write(self.success_output_all_parameters % self.id)
            self.response.out.write(page.footer)

    def draw_form(self):
        #Set up the default starting matrix with all Rs
        info = SpeciesInfo()
        self.response.out.write(self.form_instructions)
        self.response.out.write(self.form_header)
        self.response.out.write(self.form_table_header)
        for j in range(50):
            for i in range(50):
                index = (j * 50) + i
                self.response.out.write(self.form_button % index)
            if (j != 49):
                self.response.out.write('
')
        self.response.out.write(self.form_table_footer)
        plant_codes_list = []
        for i in range(1,6):
            plant_code = self.request.get('plant_code_%s' % i)
            if (plant_code != '-1'):
                plant_codes_list.append(plant_code)
        plant_codes_count = len(plant_codes_list)
        self.response.out.write(self.form_cell_value_selector_header)
        for i in range(0,plant_codes_count):
            self.response.out.write(self.form_cell_value_selector_option %
                                    (i+1, info.common_names[int(plant_codes_list[i])]))
        self.response.out.write(self.form_cell_value_selector_footer)
        self.response.out.write(self.form_assign_cells_button)
        self.response.out.write(self.form_assign_area_button)
        self.response.out.write(self.form_submit_button)
        self.response.out.write(self.form_passive_hidden_fields %
                                (self.request.get('water_level'),
                                 self.request.get('light_level'),
                                 self.request.get('temperature_level'),
                                 self.request.get('disturbance_level')))
        self.response.out.write(self.form_active_hidden_fields)
        for x in range(1, 6):
            self.response.out.write(self.form_plant_code_hidden_field %
                                    (x, self.request.get('plant_code_%s' % x)))
        self.response.out.write(self.form_footer)
        self.response.out.write(self.footer)

    def store_record(self):
        # Get a db record instance to hold the form data
        record = MeadowRecordObject()
        # Store a timestamp as the record id
        record.id = self.id
        # Store the water_level, light_level, and temperature_level,
        # disturbance_level and list of plant_species.
        record.water_level = int(self.request.get('water_level'))
        record.light_level = int(self.request.get('light_level'))
        record.temperature_level = int(self.request.get('temperature_level'))
        record.disturbance_level = int(self.request.get('disturbance_level'))
        plant_codes_list = []
        for i in range(1,6):
            plant_code = self.request.get('plant_code_%s' % i)
            if (plant_code != '-1'):
                plant_codes_list.append(plant_code)
        record.plant_types = ''
        if (len(plant_codes_list) != 0):
            record.plant_types = ','.join(plant_codes_list)
        # Store a community matrix of all 'R's
        record.starting_matrix = ''
        for i in range(2500):
            record.starting_matrix += 'R'
        record.put()

    success_output_all_parameters = """

                The simulation parameters are ready to load.

            To run the simulation use the following simulation code.

            Write it down - you will need it when you report your results.

%s

        """

    javascript = """

        """

    header = '<title>%s</title>%s'

    form_instructions = """

Year 0 plant distribution:

        Need help?
        """

    form_header = '<form enctype="multipart/form-data" action="/newparameters" method="post">'

    form_table_header = '

'

    form_button = ''

    form_table_footer = '

'

    form_cell_value_selector_header = """
        Cell value:
        <select id="cell_value">
            <option value = "R">Random plant type</option>
            <option value = "N">Permanent disturbance</option>
            <option value = "0">Gap (temporary)</option>
        """

    form_cell_value_selector_option = """
            <option value = "%s">%s</option>
        """

    form_cell_value_selector_footer = """
        </select>
        """

    form_assign_cells_button = """
        <input type="button" id="assign_cells" disabled="disabled" onclick="ApplyValueToCells(); return false;" value="Apply cell value to cells">
        """

    form_assign_area_button = """
        <input type="button" id="assign_area" disabled="disabled" onclick="ApplyValueToArea(); return false;" value="Apply cell value to area">
        """

    form_submit_button = """

<input type="submit" name="submit_value" value="Continue...">
        """

    form_passive_hidden_fields = """
        <input type="hidden" name="water_level" value="%s">
        <input type="hidden" name="light_level" value="%s">
        <input type="hidden" name="temperature_level" value="%s">
        <input type="hidden" name="disturbance_level" value="%s">
        """

    form_active_hidden_fields = """
        <input type="hidden" id="starting_matrix" name="starting_matrix" value="">
        """

    form_plant_code_hidden_field = """
        <input type="hidden" name="plant_code_%s" value="%s">
        """

    form_footer = '</form>'

    footer = ''

class StoreNewParameters(webapp2.RequestHandler):
    def post(self):
        page = HtmlPage()
        self.response.out.write(page.header % 'Simulation parameters form')
        self.id = str(int(time.time() * 1000.0))
        self.store_record()
        self.response.out.write(self.success_output_all_parameters % self.id)
        self.response.out.write(page.footer)

    def store_record(self):
        # Get a db record instance to hold the form data
        record = MeadowRecordObject()
        # Store a timestamp as the record id
        record.id = self.id
        # Store the water_level, light_level, and temperature_level,
        # disturbance_level and list of plant_species.
        record.water_level = int(self.request.get('water_level'))
        record.light_level = int(self.request.get('light_level'))
        record.temperature_level = int(self.request.get('temperature_level'))
        record.disturbance_level = int(self.request.get('disturbance_level'))
        plant_codes_list = []
        for i in range(1,6):
            plant_code = self.request.get('plant_code_%s' % i)
            if (plant_code != '-1'):
                plant_codes_list.append(plant_code)
        record.plant_types = ''
        if (len(plant_codes_list) != 0):
            record.plant_types = ','.join(plant_codes_list)
        # Store the community matrix
        # This matrix starts with 0 in the NW corner and I need 0 in the SW corner
        temp_starting_matrix = self.request.get('starting_matrix')
        upside_down_matrix = []
        for y in range(50):
            row = ''
            for x in range(50):
                row += temp_starting_matrix[y * 50 + x]
            upside_down_matrix.append(row)
        record.starting_matrix = ''
        for y in range(50):
            record.starting_matrix += upside_down_matrix[49 - y]
        record.put()

    success_output_all_parameters = """

                The simulation parameters are ready to load.

            To run the simulation use the following simulation code.

            Write it down - you will need it when you report your results.

%s

        """

class PlantPicturesPage(webapp2.RequestHandler):
    """
    Displays a page with photos of the different plant types in a new browser window.
    Accessed through links on the SetupMatrix page form.
    """
    def get(self):
        page = HtmlPage()
        info = SpeciesInfo()
        self.response.out.write(page.header % 'Species info')
        for i in range(14):
            self.response.out.write(self.plant_info_display %
                                    (info.common_names[i], info.latin_binomials[i],
                                     info.common_names[i], info.lifespans[i],
                                     info.water_levels[i], info.light_levels[i],
                                     info.temperature_levels[i], info.elevations[i],
                                     info.colonizing_levels[i], info.other_notes[i]))
        self.response.out.write(page.footer)

    plant_info_display = """

            <big>%s</big> (%s)

                    Maximum Lifespan: %s

                    Optimal Environment:

                        Water: %s

                        Light: %s

                        Temp: %s

                        Elevation: %s

                    Colonizing ability: %s

                    Other notes: %s

        """

class ShowParametersPage(webapp2.RequestHandler):
    """
    Accepts a simulation ID and returns a summary of the parameters for that ID.
    """
    def get(self):
        page = HtmlPage()
        info = SpeciesInfo()
        self.response.out.write(page.header % 'Simulation parameters')
        simulation_id = self.request.get('id')
        water_level = 2
        light_level = 2
        temperature_level = 2
        disturbance_level = 0
        #This should match the default plant types in the Unity Module
        plant_types = [1, 8, 9, 11, 13]
        plant_types_count = 5
        is_valid_id = True
        if (simulation_id != 'default'):
            data = db.GqlQuery("SELECT * FROM MeadowRecordObject WHERE id=:1",
                            simulation_id)
            if (data.count() == 1):
                # The simulation ID is valid, display the parameters
                data = data[0]
                is_valid_id = True
                water_level = data.water_level
                light_level = data.light_level
                temperature_level = data.temperature_level
                plant_types = data.plant_types.split(',')
                #remove the -1's (none's) from the list of plant types
                plant_types[:] = (value for value in plant_types if value != -1)
                plant_types_count = len(plant_types)
                disturbance_level = data.disturbance_level
                temp_starting_matrix = data.starting_matrix
                upside_down_matrix = []
                for y in range(50):
                    row = ''
                    for x in range(50):
                        row += temp_starting_matrix[y * 50 + x]
                    upside_down_matrix.append(row)
                starting_matrix = ''
                for y in range(50):
                    starting_matrix += upside_down_matrix[49 - y]
            else:
                # There is no such simulation ID (or there is more than one with that ID?)
                is_valid_id = False
                self.response.out.write(self.display_error % simulation_id)
        if (is_valid_id):
            self.response.out.write(self.display_environment_parameters %
                                    (simulation_id,
                                     self.levels[water_level],
                                     self.levels[light_level],
                                     self.levels[temperature_level],
                                     self.disturbance_levels[disturbance_level]))
            self.response.out.write(self.display_species_header)

            for i in range(0, plant_types_count):
                self.response.out.write(self.display_species %
                                        (i + 1,
                                         info.common_names[int(plant_types[i])],
                                         info.common_names[int(plant_types[i])]))
            self.response.out.write(self.display_species_footer)
            self.response.out.write(self.form_table_header)
            for j in range(50):
                for i in range(50):
                    index = (j * 50) + i
                    if (simulation_id != 'default'):
                        image = starting_matrix[index]
                    else:
                        image = 'R'
                    self.response.out.write(self.form_button % image)
                if (j != 49):
                    self.response.out.write('
')
            self.response.out.write(self.form_table_footer)
            self.response.out.write(self.form_legend)
        self.response.out.write(page.footer)

    #Conversions from the stored level integer values to human readable values
    levels = ["Lowest", "Lower", "Normal", "Higher", "Highest"]
    disturbance_levels = ["None", "Very Low","Low", "High", "Very High"]

    display_environment_parameters = """

Simulation Parameters

            Simulation ID: %s

            Water level: %s

            Light level: %s

            Temperature level: %s

            Ongoing disturbance level: %s

        """

    display_species_header = """

        """

    display_species = """

                    Species %s: %s

        """

    display_species_footer = """

            View plant details

        """

    display_error = """

Simulation ID %s does not exist!

        """

    form_table_header = 'Starting Community:

'

    form_button = ''

    form_table_footer = '

'

    form_legend = """
         = Random plant

         = Permanent disturbance

         = Gap (temporary)

         = Species 1

         = Species 2

         = Species 3

         = Species 4

         = Species 5

        """

class GetParameters(webapp2.RequestHandler):
    """
    Returns the community record with a particular timestamp as XML.
    Accessed by the vMeadow opensim module.
    """
    def get(self):
        data = db.GqlQuery("SELECT * FROM MeadowRecordObject WHERE id=:1",
                            self.request.get('id'))
        if (data.count() == 1):
            #if there are no records or more than one, something has gone wrong and
            #we are better off NOT sending anything.
            self.response.out.write(data[0].to_xml())

class RequestPlot(webapp2.RequestHandler):
    """
    Accepts data for plotting and returns an interactive dygraph-based plot.
    """
    def get(self):
        #This should only ever get called with post data but this helps with testing
        self.post()

    def post(self):
        plot_type = self.request.get('plot_type')
        simulation_id = self.request.get('simulation_id')
        if (plot_type == 'counts'):
            data_string = self.request.get('data_string')
            self.request_counts_plot(simulation_id, data_string)
        elif (plot_type == 'age'):
            data_string = self.request.get('data_string')
            self.request_ages_plot(simulation_id, data_string)
        elif (plot_type == 'biomass'):
            data_string = self.request.get('data_string')
            self.request_biomass_plot(simulation_id, data_string)
        else:
            self.response.out.write("Invalid plot type request")

    plot_creation_code = """

                <title>%s Plot</title>

Simulation ID: %s

        """

    # Debugging this can be nasty.  Keep this example of a good plot string for testing purposes.
    plot_data_string_TEST = """
        "year,Gaps,Alder,Fern,Cottonwood,Sagebrush,Pine\\n" +
        "0,135,75,10,15,45,120\\n" + "1,163,25,45,34,23,110\\n" +
        "2,174,26,67,45,65,23\\n" + "3,120,18,75,25,64,98\\n" +
        "4,58,76,100,25,48,93\\n" + "5,138,70,97,34,35,26\\n" +
        "6,154,86,25,35,45,55\\n" + "7,220,32,22,51,43,32\\n" +
        "8,167,35,43,45,54,56\\n" + "9,120,38,64,46,75,57\\n" +
        "10,118,40,73,37,84,48\\n" + "11,55,65,10,70,110,90\\n" +
        "12,180,80,34,36,38,32\\n"
        """

    def request_counts_plot(self, simulation_id, plot_data_string):
        self.response.out.write(self.plot_creation_code % (
                                'Count',
                                simulation_id,
                                plot_data_string,
                                'Counts by Species',
                                '# of individuals'))

    def request_ages_plot(self, simulation_id, plot_data_string):
        self.response.out.write(self.plot_creation_code % (
                                'Age',
                                simulation_id,
                                plot_data_string,
                                'Average Age by Species',
                                'average age'))

    def request_biomass_plot(self, simulation_id, plot_data_string):
         self.response.out.write(self.plot_creation_code % (
                                'Biomass',
                                simulation_id,
                                plot_data_string,
                                'Biomass by Species',
                                '% of total biomass'))

class SendCrossDomain(webapp2.RequestHandler):
    """
    Responds to crossdomain permission requests.
    """

    def get(self):
        crossdomain = '<?xml version="1.0"?>'
        crossdomain += '<cross-domain-policy>'
        crossdomain += '<allow-access-from domain="*"/>'
        crossdomain += '</cross-domain-policy>'
        self.response.headers['Content-Type'] = 'text/xml'
        self.response.out.write(crossdomain)

class ShowMapHelpPage(webapp2.RequestHandler):
    """
    Displays instructions for setting the starting plant distribution.
    """

    def get(self):
        page = HtmlPage()
        self.response.out.write(page.header % 'Map Instructions')
        self.response.out.write(self.instructions)
        self.response.out.write(page.footer)

    instructions = """

Using the map to set the starting plant distribution

        To put a specific plant (or gap) at specific locations:

Click on one or more locations on the map.

Select the plant type (or gap) from the drop-down menu.

Click the button to Apply cell value to cells.

Repeat 1-3 to put other plant types (or gaps) in other locations.

        To put a specific plant (or gap) in a rectangular area:

Click on exactly two locations on the map to define the corners of a rectangular area.

Select the plant type (or gap) from the drop-down menu.

Click the button to Apply cell value to area.

Repeat 1-3 to put other plant types (or gaps) in other areas.

        Note: By default, any locations you leave blank will be planted at random.
        """

# url to class mapping
application = webapp2.WSGIApplication([
    ('/', ParametersFormPageOne),
    ('/parametersform2', ParametersFormPageTwo),
    ('/newparameters', StoreNewParameters),
    ('/data', GetParameters),
    ('/requestplot', RequestPlot),
    ('/plants', PlantPicturesPage),
    ('/show', ShowParametersPage),
    ('/map-help', ShowMapHelpPage),
    ('/crossdomain.xml', SendCrossDomain)], debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()


from __future__ import division
import numpy as np

from sklearn.svm import SVC
from scipy.special import expit
import copy

from confident_classifier import ConfidentClassifier

class OvoClassifier(object):
    def __init__(self, base_classifier=SVC(kernel='linear')):
        self._base_classifier = base_classifier
        self._classifiers = []
        self._combinations = []
        self._n_classes = 0

    def fit(self, X, y, total_classes=0):
        classes = np.unique(y)
        n_classes = np.alen(classes)
        if total_classes > 0:
            self._n_classes = total_classes
        else:
            self._n_classes = n_classes
        self._combinations = np.array([[c1, c2] for i, c1 in enumerate(classes)
                                      for c2 in classes[i+1:]])
        for index, combination in enumerate(self._combinations):
            c = copy.deepcopy(self._base_classifier)
            indices = np.in1d(y, combination)
            y_train = y[indices]
            c.fit(X[indices], (y_train == combination[1]).astype(int))
            self._classifiers.append(c)

    def predict_proba(self, X):
        n = np.alen(X)
        confidences = np.ones((n, self._n_classes))
        for index, combination in enumerate(self._combinations):
            probas = self._classifiers[index].predict_proba(X)
            old_conf = confidences[:, combination]
            confidences[:, combination] = old_conf * probas
        return confidences / (confidences.sum(axis=1).reshape(-1, 1))

    def predict(self, X, mu=None, m=None):
        if type(self._base_classifier) is ConfidentClassifier:
            return self.predict_bc(X, mu=mu, m=m)
        else:
            return self.predict_non_bc(X)

    def predict_non_bc(self, X):
        n = np.alen(X)
        votes = np.zeros((n, self._n_classes))
        confidences = np.ones((n, self._n_classes))*2
        for index, combination in enumerate(self._combinations):
            predictions = combination[self._classifiers[index].predict(X)]
            votes[range(n), predictions] += 1
            value = expit(self._classifiers[index].decision_function(X))
            confidences[range(n), predictions] = np.minimum(confidences[
                                                                range(n),
                                                                predictions],
                                                            value)
        predictions = votes.argmax(axis=1)
        confidences = confidences[range(n), predictions]
        return [predictions, confidences]

    def predict_bc(self, X, mu=None, m=None):
        n = np.alen(X)
        votes = np.zeros((n, self._n_classes))
        confidences = np.ones((n, self._n_classes))*2
        check_probs = np.ones((n, self._n_classes))*2
        for index, combination in enumerate(self._combinations):
            probas = self._classifiers[index].predict_proba(X, mu=mu, m=m)
            winners = np.argmax(probas[:, :-1], axis=1)
            predictions = combination[winners]
            votes[range(n), predictions] += 1
            old_conf = confidences[range(n), predictions]
            new_conf = probas[range(n), winners]
            confidences[range(n), predictions] = np.minimum(old_conf, new_conf)

            old_check = confidences[range(n), predictions]
            new_check = 1.0 - probas[:, -1]
            check_probs[range(n), predictions] = np.minimum(old_check,
                                                            new_check)
        predictions = votes.argmax(axis=1)
        confidences = confidences[range(n), predictions]
        check_probs = check_probs[range(n), predictions]
        return [predictions, confidences, check_probs]

    @property
    def n_classes(self):
        return self._n_classes

    @property
    def classifier_type(self):
        return type(self._base_classifier)


# -*- coding: utf-8 -*
from emailer import Emailer
from datetime import datetime
from main_program import WenJuanXing
from my_logger import logger
from settings import URL, START_TIME

def fill_form(url):
    """
    填写问卷星
    """
    w = WenJuanXing(url)
    try:
        # 接收提交表单后的结果
        response_text, response_status = w.run()
        return response_text, response_status
    except Exception as e:
        LOGGER.debug(e)
        print('main.py raise a error: {}'.format(e))
        return None

def send_email(title, content):
    email = Emailer(title, content)
    email.run()

def main():
    # 获取当前时间
    now = str(datetime.now())[:-7]
    print('当前时间是：{}'.format(now))
    ret = fill_form(URL)
    # 判断是否程序出错
    if ret:
        title = '问卷星填写结果'
        content = '当前时间：{}\nresponse text:{}\nresponse status code:{}'.format(now, *ret)
        send_email(title, content)

if __name__ == '__main__':
    main()


Python = ['Andrew', 'Mitchel','David','Mike', 'Tom','Albert', 'Ali','Saad'] # list for python students
Web_Application = ['Nick','Jerry','Ali','Khan','Tom','Shah','Saad','Mike'] # list for Web Application students
pl= len(Python) # length of 1st list that will be use in for loop for iterations
wl = len(Web_Application) # length of second list to be used in for loops
Common_Students =[] # list that will contain common students
not_common= [] # list that will contain students that are not common in both classes

for j in range (0,pl): # for loops to find common elements in two lists
    for k in range(0,wl):
        if Python[j] == Web_Application[k]: # logic to find common elements
            Common_Students.append(Python[j])
for a in Python: # comparing common students with python to find the students that are  only taking python
    if a not in Common_Students:
        not_common.append(a)
for b in Web_Application: # comparing common students with Web_Application to find the students that are only taking Web Application
    if b not in Common_Students:
       not_common.append(b)
#Now printing all these results on the screen
print( "The students that are taking Python class are: ", Python)
print("The students that are taking Web Application class are: ", Web_Application)
print("Students that are taking both, Python and Web Application, are: ",Common_Students)
print("Students that are not common in both the classes are : ", not_common)

Assignment 1 lab 1/Lab 1- Source/Q4 Lab Assignment 1.py


# Lector de ficheros de descripcion de problema
# DoYouKnowDaWae?
# 27 Febrero 2018

from InOut import lectura_problema
from InOut import Configuracion
from montCaches import mont_caches
from Cache import Cache
from Indice import Indice

while True:
    nombre_fichero = input("Introduce fichero de entrada: ")

    configuracion = lectura_problema(nombre_fichero)

    tamanyo_cache = configuracion.sizeCache()
    num_caches = configuracion.num_caches()
    num_end_points = configuracion.num_endpoints()

    lista_end_points = []

    for i in range(0, num_end_points):
        end_point = Endpoint()
        lista_end_points.append(end_point)

    lista_caches = []

    indice_invertido = Indice(configuracion.get_caches_endopoints())

    for i in range(0,num_caches):
        lista_id_end_points = indice_invertido.get(i)
        lista_end_points_para_cache = []
        for j in lista_id_end_points:
            lista_end_points_para_cache.append(lista_end_points[j])
        cache = Cache(i, tamanyo_cache, lista_end_points_para_cache)
        lista_caches.append(cache)

    monticulo = mont_caches(lista_caches)

    fin = 1
    while fin != None :
        fin = monticulo.get_best()

    solucion = []
    for i in range(0,num_caches):
        soluciones_locales = lista_caches[i].get_solucion()
        solucion.apend(soluciones_locales)

    almacenar_solucion(solucion)


class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        rows = [set() for row in range(9)]
        cols = [set() for col in range(9)]
        boxes = [set() for box in range(9)]
        for row in range(9):
            for col in range(9):
                num = board[row][col]
                if num != '.':
                    n = int(num)
                    box_index = ( row // 3 ) * 3 + col // 3
                    if n in rows[row] or n in cols[col] or n in boxes[box_index]:
                        return False
                    rows[row].add(n)
                    cols[col].add(n)
                    boxes[box_index].add(n)
        return True


class Solution:
    def searchInRow(self, row, target):
        if row is None:
            return False

        n = len(row)
        if n == 0:
            return False

        if n == 1:
            return row[0] == target

        start, end = 0, n - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if row[mid] < target:
                start = mid
            elif row[mid] == target:
                return True
            else:
                end = mid

        return row[start] == target or row[end] == target

    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if matrix is None:
            return False

        nrow = len(matrix)
        if nrow == 0:
            return False

        ncol = len(matrix[0])
        for row in range(nrow):
            if len(matrix[row]) > 0 and matrix[row][0] <= target <= matrix[row][-1]:
                search_result = self.searchInRow(matrix[row], target)
                if search_result:
                    return True

        return False

2019/algorithms/0240-medium-Searcha2DMatrixII-(binary search, divide conquer).py


"""
This script evaluates an encoding model built from the features of a pretrained
PredNet on a held-out, validation set of fMRI responses in 9 visual regions
across all 10 subjects.
"""

import os
import glob
import numpy as np
import matplotlib.pyplot as plt
from load_prednet import load_prednet
from extract_features import extract_activations, apply_pca
from algonauts.utils import perform_encoding

model_dir = '/Algonauts_2021_Models/prednet/model_data_keras2/'
video_dir = '/Algonauts_2021_Models/AlgonautsVideos268_All_30fpsmax'
fmri_dir = '/Algonauts_2021_Models/participants_data_v2021/mini_track'
results_dir = "/Algonauts_2021_Models/prednet/fmri_predictions"

prednet = load_prednet(model_dir)
video_list = glob.glob(video_dir + '/*.mp4')
video_list.sort()
n_train_videos = 1000
train_activations = extract_activations(prednet, video_list[:n_train_videos])
train_features = apply_pca(train_activations)

subs = ["sub01", "sub02", "sub03", "sub04", "sub05", "sub06", "sub07", "sub08", "sub09", "sub10"]
ROIs = ["V1", "V2", "V3", "V4", "LOC", "EBA", "FFA", "STS", "PPA"]
voxelwise_corrs = np.zeros((len(subs), len(ROIs)))
for i, sub in enumerate(subs):
    for j, ROI in enumerate(ROIs):
        voxelwise_corrs[i, j] = perform_encoding(train_features,
                                                 fmri_dir,
                                                 sub=sub,
                                                 ROI=ROI)

np.save(results_dir, voxelwise_corrs)

# mean and std correlation across subjects
subjs_mean = np.mean(voxelwise_corrs, axis=0)
subjs_std = np.std(voxelwise_corrs, axis=0)

# plot results
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(ROIs, subjs_mean, color='blue', alpha=0.5)
ax.errorbar(x=ROIs, y=subjs_mean, yerr=subjs_std, linestyle='', elinewidth=1, capsize=6, color='k')
ax.set_title('Layer 4', fontsize=30, fontweight='bold', pad=35)
ax.set_xlabel("Region of interest", fontsize=25, labelpad=30)
ax.set_ylabel("Correlation", fontsize=25, labelpad=30)
ax.tick_params(axis='x', labelsize=20)
ax.tick_params(axis='y', labelsize=20)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)


# 1)написати функцію на замикання котра буде в собі зберігати список справ, вам потрібно реалізувати два методи:
# - перший записує в список нову справу
#                                - другий повертає всі записи

def notebook():
    todos_list: list[str] = []

    def add_todo(todo: str) -> None:
        nonlocal todos_list
        todos_list.append(todo)

    def get_all() -> list[str]:
        nonlocal todos_list
        return todos_list

    return add_todo, get_all

add1, all1 = notebook()
add2, all2 = notebook()

add1('awake')
add1('awake')

add2('luck')
add2('hug')

print(all1())
print(all2())

# 3) створити функцію котра буде повертати сумму розрядів числа у вигляді строки (також використовуемо типізацію)

# Приклад:
# expanded_form(12) # return '10 + 2'
# expanded_form(42) # return '40 + 2'
# expanded_form(70304) # return '70000 + 300 + 4'

def expanded_form(number: int) -> str:
    st = str(number)
    length = len(st) - 1
    res: list[str] = []

    for i, v in enumerate(st):
        if v != '0':
            res.append(v + '0' * (length - i))

    return ' + '.join(res) + f' = {st}'

print(expanded_form(500143))

# 4) створити декоратор котрий буде підраховувати скільки разів була запущена функція продекорована цим декоратором,
# та буде виводити це значення після виконання функцій

def counter(func):
    count = 0

    def inner():
        nonlocal count

        func()
        count += 1
        print(f'counter is {count}')

        print('*' * 20)
        print('\n')

    return inner

@counter
def adder1():
    print("ADDER 1 ")

@counter
def adder2():
    print("ADDER 2")

adder1()
adder2()
adder1()


import os
import numpy as np
from PIL import Image
import torch
from torch.utils.data import Dataset
from torchvision.datasets import ImageFolder

def get_dataset(data: str, train: bool, transform=None,
                target_transform=None, default_data_path=None, image_size=None) -> Dataset:
    if data == "cub":
        return CUB(default_data_path, train, transform, target_transform)
    elif data == "dtd":
        return DTD(default_data_path, train, transform, target_transform)
    elif data == "quickdraw":
        return QuickDraw(default_data_path, train, transform, target_transform)
    elif data == "stanford_cars":
        return StanfordCars(default_data_path, train, transform, target_transform)
    elif data == "tiny_imagenet":
        return TinyImageNet(default_data_path, train, transform, target_transform)
    else:
        raise NotImplementedError()

class NumpyDataset(Dataset):
    def __init__(self, default_data_path, image_path, label_path, transform=None, target_transform=None):
        super().__init__()
        self.transform = transform
        self.target_transform = target_transform
        self.image_path = os.path.join(default_data_path, image_path)
        self.images = np.load(os.path.join(default_data_path, image_path))
        self.labels = np.load(os.path.join(default_data_path, label_path))
        self.length = len(self.labels)

    def __getitem__(self, index):
        img = Image.fromarray(self.images[index])
        if 'quickdraw' in self.image_path:
            img = img.convert('RGB')
        label = self.labels[index]
        if self.transform:
            img = self.transform(img)
        if self.target_transform:
            label = self.target_transform(label)
        return img, label

    def __len__(self):
        return self.length

class CUB(NumpyDataset):
    def __init__(self, default_data_path, train=True, transform=None, target_transform=None):
        super().__init__(
            default_data_path=default_data_path,
            image_path="cub/{}_images.npy".format("train" if train else "test"),
            label_path="cub/{}_labels.npy".format("train" if train else "test"),
            transform=transform,
            target_transform=target_transform,
        )

class DTD(NumpyDataset):
    def __init__(self, default_data_path, train=True, transform=None, target_transform=None):
        super().__init__(
            default_data_path=default_data_path,
            image_path="dtd/{}_images.npy".format("train" if train else "test"),
            label_path="dtd/{}_labels.npy".format("train" if train else "test"),
            transform=transform,
            target_transform=target_transform,
        )

class QuickDraw(NumpyDataset):
    def __init__(self, default_data_path, train=True, transform=None, target_transform=None):
        super().__init__(
            default_data_path=default_data_path,
            image_path="quickdraw/{}_images.npy".format("train" if train else "test"),
            label_path="quickdraw/{}_labels.npy".format("train" if train else "test"),
            transform=transform,
            target_transform=target_transform,
        )

class StanfordCars(NumpyDataset):
    def __init__(self, default_data_path, train=True, transform=None, target_transform=None):
        super().__init__(
            default_data_path=default_data_path,
            image_path="stanford_cars/{}_images.npy".format("train" if train else "test"),
            label_path="stanford_cars/{}_labels.npy".format("train" if train else "test"),
            transform=transform,
            target_transform=target_transform,
        )

class TinyImageNet(NumpyDataset):
    def __init__(self, default_data_path, train=True, transform=None, target_transform=None):
        super().__init__(
            default_data_path=default_data_path,
            image_path="tiny_imagenet/{}_images.npy".format("train" if train else "valid"),
            label_path="tiny_imagenet/{}_labels.npy".format("train" if train else "valid"),
            transform=transform,
            target_transform=target_transform,
        )


# Efficient Solution:
# Notes: Use a trie data structure to store the given words and use back tracking on each tile of the board to see if the given words exist in the board
# Time  Complexity: O(rows*cols*4^(words.length))
class TrieNode:
    def __init__(self):
        self.children = {}
        self.isWord = False

    def addWord(self, word):
        cur = self
        for char in word:
            if char not in cur.children:
                cur.children[char] = TrieNode()
            cur = cur.children[char]

        cur.isWord = True

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        root = TrieNode()
        for word in words:
            root.addWord(word)

        rows, cols = len(board), len(board[0])
        matches, visited = set(), set()

        def dfs(row, col, node, word):
            if (row < 0 or col < 0 or
                row == rows or col == cols or
                    (row, col) in visited or board[row][col] not in node.children):
                return

            visited.add((row, col))

            node = node.children[board[row][col]]
            word += board[row][col]
            if node.isWord:
                matches.add(word)

            dfs(row + 1, col, node, word)
            dfs(row - 1, col, node, word)
            dfs(row, col + 1, node, word)
            dfs(row, col - 1, node, word)

            visited.remove((row, col))

        for row in range(rows):
            for col in range(cols):
                dfs(row, col, root, "")

        return list(matches)

# Inefficient Solution:
class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        rows = len(board)
        cols = len(board[0])
        matches = []

        def dfs(row, col, charIdx, word, path):
            if charIdx == len(word):
                return True

            if (row < 0 or col < 0
                or row >= rows or col >= cols
                or word[charIdx] != board[row][col]
                    or (row, col) in path):
                return False

            path.add((row, col))

            result = (dfs(row + 1, col, charIdx + 1, word, path) or
                      dfs(row - 1, col, charIdx + 1, word, path) or
                      dfs(row, col + 1, charIdx + 1, word, path) or
                      dfs(row, col - 1, charIdx + 1, word, path))

            path.remove((row, col))

            return result

        for word in words:
            for row in range(rows):
                for col in range(cols):
                    if dfs(row, col, 0, word, set()):
                        matches.append(word)

        return sorted(set(matches))


# 47. Permutations II
# Medium

# Given a collection of numbers that might contain duplicates, return all possible unique permutations.

# Example:

# Input: [1,1,2]
# Output:
# [
#   [1,1,2],
#   [1,2,1],
#   [2,1,1]
# ]

class Solution(object):
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        result = []

        def _helper(S, slate):
            # Base Case
            if not S:
                result.append(slate[:])
                return

            # Recursive Case
            for i in range(len(S)):
                if i > 0 and S[i] == S[i-1]:
                    continue
                slate.append(S[i])
                _helper(S[:i]+S[i+1:], slate)
                slate.pop()

        _helper(nums,[])
        return result


import sys

def min_amount_of_coins(amount, coins):
  coins.sort()
  dp = [[sys.maxsize if i>0 else 0 for j in coins] for i in range(amount+1)]

  for i in range(1, amount+1):
    for j in range(0, len(coins)):
      if i >= coins[j]:
        dp[i][j] = min(1 + dp[i - coins[j]][j], dp[i][j-1])
      else:
        dp[i][j] = dp[i][j-1]

  return dp[-1][-1]

coins = [3, 5, 8, 10]
for amount in range(41):
  print(amount, min_amount_of_coins(amount, coins))


import LibraryAppend

Text_Replace_Number = 1
Temporary_Text_Storage = []

def Translation_Code(Code_List):

    num1 = 0
    num2 = 0
    num3 = 0
    Text_Code = ""

    List_Code = Code_List
    Progress = len(List_Code) * 4
    Single = 1 / Progress
    lattice = Progress / 10

    Cumulative = 0

    print("正在解释:",end = "")

    for Row in List_Code:

        List_Code[num1] = Text_Removal(Row)

        Cumulative += 1
        if Cumulative > lattice:
            print("■■",end = "")
            Cumulative = 0
        num1 += 1

    for Row1 in List_Code:

        List_Code[num2] = Replace_Base(Row1)
        List_Code[num2] = Replace_Core(List_Code[num2])
        List_Code[num2] = Replace_Method(List_Code[num2])

        Cumulative += 1
        if Cumulative > lattice:
            print("■■",end = "")
            Cumulative = 0
        num2 += 1

    for Row2 in List_Code:

        List_Code[num3] = Text_Restores(Row2)

        Cumulative += 1
        if Cumulative > lattice:
            print("■■",end = "")
            Cumulative = 0
        num3 += 1

    for Row3 in List_Code:

        Text_Code = Text_Code + Row3 + "\n"

        Cumulative += 1
        if Cumulative > lattice:
            print("■■",end = "")
            Cumulative = 0
    print("■■")
    return Text_Code

def Text_Removal(Code_Row):
#逐字符查找取区间自定义文字

    global Text_Replace_Number
    global Temporary_Text_Storage

    Row_Code = Code_Row
    Recording = ""
    position = 0
    num = 0

    Row_Code = Symbol_Replacement(Row_Code)

    num1 = 0

    while True:

        if Row_Code == "":
            break

        if Recording == "":

            if Row_Code[num1] == '"""':
                Recording = '"'
                position = num

                continue

            if Row_Code[num1] == '"':
                Recording = '"'
                position = num

                continue
            if Row_Code[num1] == "'":
                Recording = "'"
                position = num

                continue
        else:

            if Row_Code[num1] == Recording:

                if position != num:

                    Interval_Text = Row_Code[position:num + 1]
                    Replace_Text = f"/Suiyue_thbs{Text_Replace_Number}thbs_Suiyue/"
                    position = 0
                    Text_Replace_Number += 1

                    Temporary_Text_Storage.append([Replace_Text,Interval_Text])

                    Row_Code = Row_Code.replace(Interval_Text,Replace_Text)

                    Recording = ""
        num += 1

        if len(Row_Code) - 1 == num1:
            break
        num1 += 1

    return Row_Code

def Text_Restores(Code_Row):
    global Temporary_Text_Storage

    Code = Code_Row
    for i in Temporary_Text_Storage:
        Code = Code.replace(i[0],i[1])
    return Code

#执行替换
def Replace_Base(Code):

    Replace_Code = Code

    for i in LibraryAppend.Code_Base:

        Replace_Code = Replace_Code.replace(i[1],i[0])

    return Replace_Code

def Replace_Core(Code):

    Replace_Code = Code

    for i in LibraryAppend.Code_Core:
        Replace_Code = Replace_Code.replace(i[1],i[0])

    return Replace_Code

def Replace_Method(Code):

    Replace_Code = Code

    for i in LibraryAppend.Code_Method:
        Replace_Code = Replace_Code.replace(i[1],i[0])

    return Replace_Code

def Symbol_Replacement(Code):
    Code_Text = Code

    Code_Text = Code_Text.replace("“",'"')
    Code_Text = Code_Text.replace("”",'"')

    Code_Text = Code_Text.replace("‘","'")
    Code_Text = Code_Text.replace("’","'")

    Code_Text = Code_Text.replace("！",'!')
    Code_Text = Code_Text.replace("？",'?')
    Code_Text = Code_Text.replace("。",'.')

    Code_Text = Code_Text.replace("（",'(')
    Code_Text = Code_Text.replace("）",')')

    Code_Text = Code_Text.replace("【",'[')
    Code_Text = Code_Text.replace("】",']')

    Code_Text = Code_Text.replace("《",'<')
    Code_Text = Code_Text.replace("》",'>')

    Code_Text = Code_Text.replace("：",':')

    return Code_Text#中文符号替换


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

class Solution:
    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head is None or head.next is None:
            return head
        odd = head
        even_head = head.next
        even = head.next
        cur = head.next
        index = 2
        while cur.next is not None:
            index += 1
            if index % 2 == 1:
                odd.next = cur.next
                odd = odd.next
            else:
                even.next = cur.next
                even = even.next
            cur = cur.next
        even.next = None
        odd.next = even_head

        return head


import os
from source.rel_path import rootDir
from source.process_spatial_data import recursive_search_target
import numpy as np
from source.atom_radii import atomRadii

_atom_translator = {
    'C' : 'C',
    'H' : 'H',
    'N' : 'N',
    'O' : 'O',
    'S' : 'S',
    'OA': 'O',
    'NA' : 'N',
    'SA' : 'S',
    'HD' : 'H',
    'A' : 'C'
}

allAtoms = list(set(_atom_translator.keys()))
allAtoms.sort()

class PDBQTItem:

    def __init__(self, allFields):
        self.nAtoms = len(allFields)
        # gather atom types
        self.atomType = [allFields[i][-1] for i in range(self.nAtoms)]

        self.elementType = [_atom_translator[atom] for atom in self.atomType]

        # gather atom coordinates
        atomLoc = [allFields[i][8:11] for i in range(self.nAtoms)]
        self.atomLoc = np.asarray(atomLoc)

        self.atomCenter = np.mean(self.atomLoc, axis=0)

    def get_grid_edges(self, nx, ny, nz):
        halfx = (nx - 1) / 2
        halfy = (ny - 1) / 2
        halfz = (nz - 1) / 2

        edges = [
            self.atomCenter[0] - halfx, self.atomCenter[0] + halfx,
            self.atomCenter[1] - halfy, self.atomCenter[1] + halfy,
            self.atomCenter[2] - halfz, self.atomCenter[2] + halfz
        ]

        return np.asarray(edges).reshape((3, 2))

_pdbqt_ticks = [(1,6), (7,11), (13,16), (17, 17), (18, 21), (22, 22), (23, 26), (27, 27), (31,38), (39, 46), (47, 54),
                (55, 60), (61, 66), (67, 76), (77, 80)]

_identity = lambda x : x
_fieldFuncs = [_identity, int, _identity, _identity, _identity, _identity, int, _identity, float, float, float, float, float, float, _identity]

def parse_pdbqt(filename):

    with open(filename, 'r') as infile:
        lines = infile.readlines()

    allFields = []
    for line in lines:
        line = line.strip()
        if line.startswith('TER'):
            continue

        fields = []
        for l, r in _pdbqt_ticks:
            field = line[l - 1 : r]
            fields.append(field.strip())

        assert len(fields) == 15
        parsedFields = [_fieldFuncs[i](fields[i]) for i in range(len(fields))]
        allFields.append(parsedFields)

    return PDBQTItem(allFields)

if __name__ == '__main__':
    target = os.path.join(rootDir, 'data', 'test', '3D', '1kzy', '1kzy.pdbqt')
    result = parse_pdbqt(target)
    print(result.atomCenter)
    print(result.get_grid_edges(15, 15, 15))