from __future__ import absolute_import, division, print_function
from six.moves import range
from dials.array_family import flex
from dials.util import show_mail_on_error
from scitbx.matrix import col
import numpy as np
from libtbx.phil import parse
import libtbx.load_env
import math
from six.moves import zip
from serialtbx.detector import iterate_detector_at_level, iterate_panels, id_from_name, get_center

try:
  from matplotlib import pyplot as plt
  from matplotlib.patches import Polygon
  from matplotlib.colors import Normalize
  from matplotlib import cm
except ImportError:
  pass # Can happen on non-GUI nodes

help_message = '''

This program is used to calculate statisical measurements of consistency
between two detectors.

Example:

  %s experiment1.expt experiment2.expt reflections1.refl reflections2.refl
''' % libtbx.env.dispatcher_name

# Create the phil parameters
phil_scope = parse('''
tag = None
  .type = str
  .help = Used in the plot titles
hierarchy_level=0
  .type=int
  .help=Provide congruence statistics for detector modules at the given hierarchy level.
colormap=RdYlGn_r
  .type=str
  .help=matplotlib color map. See e.g.: \
        http://matplotlib.org/examples/color/colormaps_reference.html
show_plots=True
  .type=bool
  .help=Whether to show congruence plots
draw_normal_arrows=False
  .type=bool
  .help=Whether to draw the XY components of each panel group's normal vector. Useful \
        for visualizing tilt.
''')

def get_bounds(root, pg):
  """ Find the max extent of the panel group pg, projected onto the fast/slow plane of root """
  def panel_bounds(root, panel):
    size = panel.get_image_size()
    p0 = col(panel.get_pixel_lab_coord((0,0)))
    p1 = col(panel.get_pixel_lab_coord((size[0]-1,0)))
    p2 = col(panel.get_pixel_lab_coord((size[0]-1,size[1]-1)))
    p3 = col(panel.get_pixel_lab_coord((0,size[1]-1)))

    rn = col(root.get_normal())
    rf = col(root.get_fast_axis())
    rs = col(root.get_slow_axis())

    return [col((p.dot(rf), + p.dot(rs),0)) for p in [p0, p1, p2, p3]]

  if pg.is_group():
    minx = miny = float('inf')
    maxx = maxy = float('-inf')
    for panel in iterate_panels(pg):
      bounds = panel_bounds(root, panel)
      for v in bounds:
        if v[0] < minx:
          minx = v[0]
        if v[0] > maxx:
          maxx = v[0]
        if v[1] < miny:
          miny = v[1]
        if v[1] > maxy:
          maxy = v[1]
    return [col((minx, miny, 0)),
            col((maxx, miny, 0)),
            col((maxx, maxy, 0)),
            col((minx, maxy, 0))]

  else:
    return panel_bounds(root, pg)

def detector_plot_dict(params, detector, data, title, units_str, show=True, reverse_colormap=False):
  """
  Use matplotlib to plot a detector, color coding panels according to data
  @param detector detector reference detector object
  @param data python dictionary of panel names as keys and numbers as values
  @param title title string for plot
  @param units_str string with a formatting statment for units on each panel
  """
  # initialize the color map
  values = flex.double(list(data.values()))
  norm = Normalize(vmin=flex.min(values), vmax=flex.max(values))
  cmap = plt.get_cmap(params.colormap + ("_r" if reverse_colormap else ''))
  sm = cm.ScalarMappable(norm=norm, cmap=cmap)
  if len(values) == 0:
    print("no values")
    return
  elif len(values) == 1:
    sm.set_array(np.arange(values[0], values[0], 1)) # needed for colorbar
  else:
    sm.set_array(np.arange(flex.min(values), flex.max(values), (flex.max(values)-flex.min(values))/20)) # needed for colorbar

  fig = plt.figure()
  ax = fig.add_subplot(111, aspect='equal')
  max_dim = 0
  root = detector.hierarchy()
  rf = col(root.get_fast_axis())
  rs = col(root.get_slow_axis())
  for pg_id, pg in enumerate(iterate_detector_at_level(root, 0, params.hierarchy_level)):
    if pg.get_name() not in data:
      continue
    # get panel coordinates
    p0, p1, p2, p3 = get_bounds(root, pg)

    v1 = p1-p0
    v2 = p3-p0
    vcen = ((v2/2) + (v1/2)) + p0

    # add the panel to the plot
    ax.add_patch(Polygon((p0[0:2],p1[0:2],p2[0:2],p3[0:2]), closed=True, color=sm.to_rgba(data[pg.get_name()]), fill=True))
    ax.annotate("%d %s"%(pg_id, units_str%data[pg.get_name()]), vcen[0:2], ha='center')

    if params.draw_normal_arrows:
      pgn = col(pg.get_normal())
      v = col((rf.dot(pgn), rs.dot(pgn), 0))
      v *= 10000
      ax.arrow(vcen[0], vcen[1], v[0], v[1], head_width=5.0, head_length=10.0, fc='k', ec='k')

    # find the plot maximum dimensions
    for p in [p0, p1, p2, p3]:
      for c in p[0:2]:
        if abs(c) > max_dim:
          max_dim = abs(c)

  # plot the results
  ax.set_xlim((-max_dim,max_dim))
  ax.set_ylim((-max_dim,max_dim))
  ax.set_xlabel("mm")
  ax.set_ylabel("mm")
  fig.colorbar(sm)
  plt.title(title)
  if show:
    plt.show()

class Script(object):
  ''' Class to parse the command line options. '''

  def __init__(self):
    ''' Set the expected options. '''
    from dials.util.options import ArgumentParser
    import libtbx.load_env

    # Create the option parser
    usage = "usage: %s experiment1.expt experiment2.expt reflections1.refl reflections2.refl" % libtbx.env.dispatcher_name
    self.parser = ArgumentParser(
      usage=usage,
      sort_options=True,
      phil=phil_scope,
      read_experiments=True,
      read_reflections=True,
      epilog=help_message)

  def run(self):
    ''' Parse the options. '''
    from dials.util.options import flatten_experiments, flatten_reflections
    # Parse the command line arguments
    params, options = self.parser.parse_args(show_diff_phil=True)
    self.params = params
    experiments = flatten_experiments(params.input.experiments)
    reflections = flatten_reflections(params.input.reflections)

    # Find all detector objects
    detectors = []
    detectors.extend(experiments.detectors())

    # Verify inputs
    if len(detectors) != 2:
      print("Please provide two experiments for comparison")
      return

    # These lines exercise the iterate_detector_at_level and iterate_panels functions
    # for a detector with 4 hierarchy levels
    """
    print "Testing iterate_detector_at_level"
    for level in range(4):
      print "iterating at level", level
      for panelg in iterate_detector_at_level(detectors[0].hierarchy(), 0, level):
        print panelg.get_name()

    print "Testing iterate_panels"
    for level in range(4):
      print "iterating at level", level
      for panelg in iterate_detector_at_level(detectors[0].hierarchy(), 0, level):
        for panel in iterate_panels(panelg):
          print panel.get_name()
    """
    tmp = []
    for refls in reflections:
      print("N reflections total:", len(refls))
      refls = refls.select(refls.get_flags(refls.flags.used_in_refinement))
      print("N reflections used in refinement", len(refls))
      print("Reporting only on those reflections used in refinement")

      refls['difference_vector_norms'] = (refls['xyzcal.mm']-refls['xyzobs.mm.value']).norms()
      tmp.append(refls)
    reflections = tmp

    # Iterate through the detectors, computing the congruence statistics
    delta_normals = {}
    z_angles = {}
    f_deltas = {}
    s_deltas = {}
    z_deltas = {}
    o_deltas = {} # overall
    z_offsets_d = {}
    refl_counts = {}
    all_delta_normals = flex.double()
    all_rdelta_normals = flex.double()
    all_tdelta_normals = flex.double()
    all_z_angles = flex.double()
    all_f_deltas = flex.double()
    all_s_deltas = flex.double()
    all_z_deltas = flex.double()
    all_deltas = flex.double()
    all_refls_count = flex.int()

    all_normal_angles = flex.double()
    all_rnormal_angles = flex.double()
    all_tnormal_angles = flex.double()
    pg_normal_angle_sigmas = flex.double()
    pg_rnormal_angle_sigmas = flex.double()
    pg_tnormal_angle_sigmas = flex.double()
    all_rot_z = flex.double()
    pg_rot_z_sigmas = flex.double()
    pg_bc_dists = flex.double()
    all_bc_dist = flex.double()
    all_f_offsets = flex.double()
    all_s_offsets = flex.double()
    all_z_offsets = flex.double()
    pg_f_offset_sigmas = flex.double()
    pg_s_offset_sigmas = flex.double()
    pg_z_offset_sigmas = flex.double()
    pg_offset_sigmas = flex.double()
    all_weights = flex.double()

    congruence_table_data = []
    detector_table_data = []
    rmsds_table_data = []
    root1 = detectors[0].hierarchy()
    root2 = detectors[1].hierarchy()

    s0 = col(flex.vec3_double([col(b.get_s0()) for b in experiments.beams()]).mean())

    # Compute a set of radial and transverse displacements for each reflection
    print("Setting up stats...")
    tmp_refls = []
    for refls, expts in zip(reflections, [wrapper.data for wrapper in params.input.experiments]):
      tmp = flex.reflection_table()
      assert len(expts.detectors()) == 1
      dect = expts.detectors()[0]
      # Need to construct a variety of vectors
      for panel_id, panel in enumerate(dect):
        panel_refls = refls.select(refls['panel'] == panel_id)
        bcl = flex.vec3_double()
        # Compute the beam center in lab space (a vector pointing from the origin to where the beam would intersect
        # the panel, if it did intersect the panel)
        for expt_id in set(panel_refls['id']):
          beam = expts[expt_id].beam
          s0 = beam.get_s0()
          expt_refls = panel_refls.select(panel_refls['id'] == expt_id)
          beam_centre = panel.get_beam_centre_lab(s0)
          bcl.extend(flex.vec3_double(len(expt_refls), beam_centre))
        panel_refls['beam_centre_lab'] = bcl

        # Compute obs in lab space
        x, y, _ = panel_refls['xyzobs.mm.value'].parts()
        c = flex.vec2_double(x, y)
        panel_refls['obs_lab_coords'] = panel.get_lab_coord(c)
        # Compute deltaXY in panel space. This vector is relative to the panel origin
        x, y, _ = (panel_refls['xyzcal.mm'] - panel_refls['xyzobs.mm.value']).parts()
        # Convert deltaXY to lab space, subtracting off of the panel origin
        panel_refls['delta_lab_coords'] = panel.get_lab_coord(flex.vec2_double(x,y)) - panel.get_origin()
        tmp.extend(panel_refls)
      refls = tmp
      # The radial vector points from the center of the reflection to the beam center
      radial_vectors = (refls['obs_lab_coords'] - refls['beam_centre_lab']).each_normalize()
      # The transverse vector is orthogonal to the radial vector and the beam vector
      transverse_vectors = radial_vectors.cross(refls['beam_centre_lab']).each_normalize()
      # Compute the raidal and transverse components of each deltaXY
      refls['radial_displacements']     = refls['delta_lab_coords'].dot(radial_vectors)
      refls['transverse_displacements'] = refls['delta_lab_coords'].dot(transverse_vectors)

      tmp_refls.append(refls)
    reflections = tmp_refls

    for pg_id, (pg1, pg2) in enumerate(zip(iterate_detector_at_level(root1, 0, params.hierarchy_level),
                                           iterate_detector_at_level(root2, 0, params.hierarchy_level))):
      """ First compute statistics for detector congruence """
      # Count up the number of reflections in this panel group pair for use as a weighting scheme
      total_refls = 0
      pg1_refls = 0
      pg2_refls = 0
      for p1, p2 in zip(iterate_panels(pg1), iterate_panels(pg2)):
        r1 = len(reflections[0].select(reflections[0]['panel'] == id_from_name(detectors[0], p1.get_name())))
        r2 = len(reflections[1].select(reflections[1]['panel'] == id_from_name(detectors[1], p2.get_name())))
        total_refls += r1 + r2
        pg1_refls += r1
        pg2_refls += r2
      if pg1_refls == 0 and pg2_refls == 0:
        print("No reflections on panel group", pg_id)
        continue

      assert pg1.get_name() == pg2.get_name()
      refl_counts[pg1.get_name()] = total_refls

      row = ["%d"%pg_id]
      for pg, refls, det in zip([pg1, pg2], reflections, detectors):
        pg_refls = flex.reflection_table()
        for p in iterate_panels(pg):
          pg_refls.extend(refls.select(refls['panel'] == id_from_name(det, p.get_name())))
        if len(pg_refls) == 0:
          rmsd = r_rmsd = t_rmsd = 0
        else:
          rmsd = math.sqrt(flex.sum_sq(pg_refls['difference_vector_norms'])/len(pg_refls))*1000
          r_rmsd = math.sqrt(flex.sum_sq(pg_refls['radial_displacements'])/len(pg_refls))*1000
          t_rmsd = math.sqrt(flex.sum_sq(pg_refls['transverse_displacements'])/len(pg_refls))*1000

        row.extend(["%6.1f"%rmsd, "%6.1f"%r_rmsd, "%6.1f"%t_rmsd, "%8d"%len(pg_refls)])
      rmsds_table_data.append(row)

      # Angle between normals of pg1 and pg2
      delta_norm_angle = col(pg1.get_normal()).angle(col(pg2.get_normal()), deg=True)
      all_delta_normals.append(delta_norm_angle)

      # compute radial and transverse components of the delta between normal angles
      pgo = (get_center(pg1)+get_center(pg2))/2
      ro = (get_center(root1)+get_center(root2))/2
      rn = (col(root1.get_normal())+col(root2.get_normal()))/2
      rf = (col(root1.get_fast_axis())+col(root2.get_fast_axis()))/2
      rs = (col(root1.get_slow_axis())+col(root2.get_slow_axis()))/2

      ro_pgo = pgo - ro # vector from the detector origin to the average panel group origin
      if ro_pgo.length() == 0:
        radial = col((0,0,0))
        transverse = col((0,0,0))
      else:
        radial = ((rf.dot(ro_pgo) * rf) + (rs.dot(ro_pgo) * rs)).normalize() # component of ro_pgo in rf rs plane
        transverse = rn.cross(radial).normalize()
      # now radial and transverse are vectors othogonal to each other and the detector normal, such that
      # radial points at the panel group origin
      # v1 and v2 are the components of pg 1 and 2 normals in the rn radial plane
      v1 = (radial.dot(col(pg1.get_normal())) * radial) + (rn.dot(col(pg1.get_normal())) * rn)
      v2 = (radial.dot(col(pg2.get_normal())) * radial) + (rn.dot(col(pg2.get_normal())) * rn)
      rdelta_norm_angle = v1.angle(v2, deg=True)
      if v1.cross(v2).dot(transverse) < 0:
        rdelta_norm_angle = -rdelta_norm_angle
      all_rdelta_normals.append(rdelta_norm_angle)
      # v1 and v2 are the components of pg 1 and 2 normals in the rn transverse plane
      v1 = (transverse.dot(col(pg1.get_normal())) * transverse) + (rn.dot(col(pg1.get_normal())) * rn)
      v2 = (transverse.dot(col(pg2.get_normal())) * transverse) + (rn.dot(col(pg2.get_normal())) * rn)
      tdelta_norm_angle = v1.angle(v2, deg=True)
      if v1.cross(v2).dot(radial) < 0:
        tdelta_norm_angle = -tdelta_norm_angle
      all_tdelta_normals.append(tdelta_norm_angle)

      # compute the angle between fast axes of these panel groups
      z_angle = col(pg1.get_fast_axis()[0:2]).angle(col(pg2.get_fast_axis()[0:2]), deg=True)
      all_z_angles.append(z_angle)
      z_angles[pg1.get_name()] = z_angle

      all_refls_count.append(total_refls)
      all_weights.append(pg1_refls)
      all_weights.append(pg2_refls)

      """ Now compute statistics measuring the reality of the detector. For example, instead of the distance between two things,
      we are concerned with the location of those things relative to laboratory space """
      # Compute distances between panel groups and beam center
      # Also compute offset along Z axis
      dists = flex.double()
      f_offsets = flex.double()
      s_offsets = flex.double()
      z_offsets = flex.double()
      for pg, r in zip([pg1, pg2], [root1, root2]):
        bc = col(pg.get_beam_centre_lab(s0))
        ori = get_center(pg)

        dists.append((ori-bc).length())

        rori = col(r.get_origin())
        delta_ori = ori-rori
        r_norm = col(r.get_normal())
        r_fast = col(r.get_fast_axis())
        r_slow = col(r.get_slow_axis())
        f_offsets.append(r_fast.dot(delta_ori)*1000)
        s_offsets.append(r_slow.dot(delta_ori)*1000)
        z_offsets.append(r_norm.dot(delta_ori)*1000)

      fd = abs(f_offsets[0]-f_offsets[1])
      sd = abs(s_offsets[0]-s_offsets[1])
      zd = abs(z_offsets[0]-z_offsets[1])
      od = math.sqrt(fd**2+sd**2+zd**2)
      f_deltas[pg1.get_name()] = fd
      s_deltas[pg1.get_name()] = sd
      z_deltas[pg1.get_name()] = zd
      o_deltas[pg1.get_name()] = od
      all_f_deltas.append(fd)
      all_s_deltas.append(sd)
      all_z_deltas.append(zd)
      all_deltas.append(od)

      all_f_offsets.extend(f_offsets)
      all_s_offsets.extend(s_offsets)
      all_z_offsets.extend(z_offsets)

      # Compute angle between detector normal and panel group normal
      # Compute rotation of panel group around detector normal
      pg_rotz = flex.double()
      norm_angles = flex.double()
      rnorm_angles = flex.double()
      tnorm_angles = flex.double()
      for pg, r in zip([pg1, pg2], [root1, root2]):

        pgo = get_center(pg)
        pgn = col(pg.get_normal())
        pgf = col(pg.get_fast_axis())

        ro = get_center(r)
        rn = col(r.get_normal())
        rf = col(r.get_fast_axis())
        rs = col(r.get_slow_axis())

        norm_angle = rn.angle(pgn, deg=True)
        norm_angles.append(norm_angle)
        all_normal_angles.append(norm_angle)

        ro_pgo = pgo - ro # vector from the detector origin to the panel group origin
        if ro_pgo.length() == 0:
          radial = col((0,0,0))
          transverse = col((0,0,0))
        else:
          radial = ((rf.dot(ro_pgo) * rf) + (rs.dot(ro_pgo) * rs)).normalize() # component of ro_pgo in rf rs plane
          transverse = rn.cross(radial).normalize()
        # now radial and transverse are vectors othogonal to each other and the detector normal, such that
        # radial points at the panel group origin
        # v is the component of pgn in the rn radial plane
        v = (radial.dot(pgn) * radial) + (rn.dot(pgn) * rn)
        angle = rn.angle(v, deg=True)
        if rn.cross(v).dot(transverse) < 0:
          angle = -angle
        rnorm_angles.append(angle)
        all_rnormal_angles.append(angle)
        # v is the component of pgn in the rn transverse plane
        v = (transverse.dot(pgn) * transverse) + (rn.dot(pgn) * rn)
        angle = rn.angle(v, deg=True)
        if rn.cross(v).dot(radial) < 0:
          angle = -angle
        tnorm_angles.append(angle)
        all_tnormal_angles.append(angle)

        # v is the component of pgf in the rf rs plane
        v = (rf.dot(pgf) * rf) + (rs.dot(pgf) * rs)
        angle = rf.angle(v, deg=True)
        angle = angle-(round(angle/90)*90) # deviation from 90 degrees
        pg_rotz.append(angle)
        all_rot_z.append(angle)

      # Set up table rows using stats aggregated from above
      pg_weights = flex.double([pg1_refls, pg2_refls])
      if 0 in pg_weights:
        dist_m = dist_s = norm_angle_m = norm_angle_s = rnorm_angle_m = rnorm_angle_s = 0
        tnorm_angle_m = tnorm_angle_s = rotz_m = rotz_s = 0
        fo_m = fo_s = so_m = so_s = zo_m = zo_s = o_s = 0

      else:
        stats = flex.mean_and_variance(dists, pg_weights)
        dist_m = stats.mean()
        dist_s = stats.gsl_stats_wsd()

        stats = flex.mean_and_variance(norm_angles, pg_weights)
        norm_angle_m = stats.mean()
        norm_angle_s = stats.gsl_stats_wsd()

        stats = flex.mean_and_variance(rnorm_angles, pg_weights)
        rnorm_angle_m = stats.mean()
        rnorm_angle_s = stats.gsl_stats_wsd()

        stats = flex.mean_and_variance(tnorm_angles, pg_weights)
        tnorm_angle_m = stats.mean()
        tnorm_angle_s = stats.gsl_stats_wsd()

        stats = flex.mean_and_variance(pg_rotz, pg_weights)
        rotz_m = stats.mean()
        rotz_s = stats.gsl_stats_wsd()

        stats = flex.mean_and_variance(f_offsets, pg_weights)
        fo_m = stats.mean()
        fo_s = stats.gsl_stats_wsd()
        stats = flex.mean_and_variance(s_offsets, pg_weights)
        so_m = stats.mean()
        so_s = stats.gsl_stats_wsd()
        stats = flex.mean_and_variance(z_offsets, pg_weights)
        zo_m = stats.mean()
        zo_s = stats.gsl_stats_wsd()

        o_s = math.sqrt(fo_s**2+so_s**2+zo_s**2)

      pg_bc_dists.append(dist_m)
      all_bc_dist.extend(dists)
      pg_normal_angle_sigmas.append(norm_angle_s)
      pg_rnormal_angle_sigmas.append(rnorm_angle_s)
      pg_tnormal_angle_sigmas.append(tnorm_angle_s)
      pg_rot_z_sigmas.append(rotz_s)
      pg_f_offset_sigmas.append(fo_s)
      pg_s_offset_sigmas.append(so_s)
      pg_z_offset_sigmas.append(zo_s)
      pg_offset_sigmas.append(o_s)
      z_offsets_d[pg1.get_name()] = zo_m

      congruence_table_data.append(["%d"%pg_id, "%5.1f"%dist_m, #"%.4f"%dist_s,
                                    "%.4f"%delta_norm_angle, "%.4f"%rdelta_norm_angle,
                                    "%.4f"%tdelta_norm_angle, "%.4f"%z_angle,
                                    "%4.1f"%fd, "%4.1f"%sd, "%4.1f"%zd, "%4.1f"%od, "%6d"%total_refls])
      detector_table_data.append(["%d"%pg_id, "%5.1f"%dist_m, #"%.4f"%dist_s,
                                  "%.4f"%norm_angle_m, "%.4f"%norm_angle_s,
                                  "%.4f"%rnorm_angle_m, "%.4f"%rnorm_angle_s,
                                  "%.4f"%tnorm_angle_m, "%.4f"%tnorm_angle_s,
                                  "%10.6f"%rotz_m, "%.6f"%rotz_s,
                                  #"%9.1f"%fo_m, "%5.3f"%fo_s,
                                  #"%9.1f"%so_m, "%5.3f"%so_s,
                                  "%9.3f"%fo_s,
                                  "%9.3f"%so_s,
                                  "%9.1f"%zo_m, "%9.1f"%zo_s, "%9.3f"%o_s, "%6d"%total_refls])

    # Set up table output
    table_d = {d:row for d, row in zip(pg_bc_dists, congruence_table_data)}
    table_header = ["PanelG","Dist","Normal","RNormal","TNormal","Z rot","Delta","Delta","Delta","Delta","N"]
    table_header2 = ["Id","","Angle","Angle","Angle","Angle","F","S","Z","O","Refls"]
    table_header3 = ["", "(mm)","(mm)","(deg)","(deg)","(microns)","(microns)","(microns)","(microns)","(microns)",""]
    congruence_table_data = [table_header, table_header2, table_header3]
    congruence_table_data.extend([table_d[key] for key in sorted(table_d)])

    table_d = {d:row for d, row in zip(pg_bc_dists, detector_table_data)}
    table_header = ["PanelG","Dist","Normal","Normal","RNormal","RNormal","TNormal","TNormal","RotZ", "RotZ","F Offset","S Offset","Z Offset","Z Offset","Offset","N"]
    table_header2 = ["Id","","","Sigma","","Sigma","","Sigma","","Sigma","Sigma","Sigma","","Sigma","Sigma","Refls"]
    table_header3 = ["", "(mm)","(deg)","(deg)","(deg)","(deg)","(deg)","(deg)","(deg)","(deg)","(microns)","(microns)","(microns)","(microns)","(microns)",""]
    detector_table_data = [table_header, table_header2, table_header3]
    detector_table_data.extend([table_d[key] for key in sorted(table_d)])

    table_d = {d:row for d, row in zip(pg_bc_dists, rmsds_table_data)}
    table_header = ["PanelG"]
    table_header2 = ["Id"]
    table_header3 = [""]
    for i in range(len(detectors)):
      table_header.extend(["D%d"%i]*4)
      table_header2.extend(["RMSD", "rRMSD", "tRMSD", "N refls"])
      table_header3.extend(["(microns)"]*3)
      table_header3.append("")
    rmsds_table_data = [table_header, table_header2, table_header3]
    rmsds_table_data.extend([table_d[key] for key in sorted(table_d)])

    if len(all_refls_count) > 1:
      r1 = ["Weighted mean"]
      r2 = ["Weighted stddev"]
      r1.append("")
      r2.append("")
      #r1.append("")
      #r2.append("")
      stats = flex.mean_and_variance(all_delta_normals, all_refls_count.as_double())
      r1.append("%.4f"%stats.mean())
      r2.append("%.4f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_rdelta_normals, all_refls_count.as_double())
      r1.append("%.4f"%stats.mean())
      r2.append("%.4f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_tdelta_normals, all_refls_count.as_double())
      r1.append("%.4f"%stats.mean())
      r2.append("%.4f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_z_angles, all_refls_count.as_double())
      r1.append("%.4f"%stats.mean())
      r2.append("%.4f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_f_deltas, all_refls_count.as_double())
      r1.append("%4.1f"%stats.mean())
      r2.append("%4.1f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_s_deltas, all_refls_count.as_double())
      r1.append("%4.1f"%stats.mean())
      r2.append("%4.1f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_z_deltas, all_refls_count.as_double())
      r1.append("%4.1f"%stats.mean())
      r2.append("%4.1f"%stats.gsl_stats_wsd())
      stats = flex.mean_and_variance(all_deltas, all_refls_count.as_double())
      r1.append("%4.1f"%stats.mean())
      r2.append("%4.1f"%stats.gsl_stats_wsd())
      r1.append("")
      r2.append("")
      congruence_table_data.append(r1)
      congruence_table_data.append(r2)
      congruence_table_data.append(["Mean", "", "", "","","", "", "", "", "", "", "%6.1f"%flex.mean(all_refls_count.as_double())])

    from libtbx import table_utils
    print("Congruence statistics, I.E. the differences between the input detectors:")
    print(table_utils.format(congruence_table_data,has_header=3,justify='center',delim=" "))

    print("PanelG Id: panel group id or panel id, depending on hierarchy_level. For each panel group, statistics are computed between the matching panel groups between the two input experiments.")
    print("Dist: distance from center of panel group to the beam center")
    print("Dist Sigma: weighted standard deviation of the measurements used to compute Dist")
    print("Normal angle: angle between the normal vectors of matching panel groups.")
    print("RNormal angle: radial component of the angle between the normal vectors of matching panel groups")
    print("TNormal angle: transverse component of the angle between the normal vectors of matching panel groups")
    print("Z rot: angle between the XY components of the fast axes of the panel groups.")
    print("Delta F: shift between matching panel groups along the detector fast axis.")
    print("Delta S: shift between matching panel groups along the detector slow axis.")
    print("Delta Z: Z shift between matching panel groups along the detector normal.")
    print("Delta O: Overall shift between matching panel groups along the detector normal.")
    print("N refls: number of reflections summed between both matching panel groups. This number is used as a weight when computing means and standard deviations.")
    print()
    print()

    if len(all_weights) > 1:
      r1 = ["All"]
      r2 = ["Mean"]
      for data, weights, fmt in [[None,None,None],
                                 #[None,None,None],
                                 [all_normal_angles,       all_weights.as_double(),     "%.4f"],
                                 [pg_normal_angle_sigmas,  all_refls_count.as_double(), "%.4f"],
                                 [all_rnormal_angles,      all_weights.as_double(),     "%.4f"],
                                 [pg_rnormal_angle_sigmas, all_refls_count.as_double(), "%.4f"],
                                 [all_tnormal_angles,      all_weights.as_double(),     "%.4f"],
                                 [pg_tnormal_angle_sigmas, all_refls_count.as_double(), "%.4f"],
                                 [all_rot_z,               all_weights.as_double(),     "%10.6f"],
                                 [pg_rot_z_sigmas,         all_refls_count.as_double(), "%.6f"],
                                 #[all_f_offsets,           all_weights.as_double(),     "%9.1f"],
                                 [pg_f_offset_sigmas,      all_refls_count.as_double(), "%9.3f"],
                                 #[all_s_offsets,           all_weights.as_double(),     "%9.1f"],
                                 [pg_s_offset_sigmas,      all_refls_count.as_double(), "%9.3f"],
                                 [all_z_offsets,           all_weights.as_double(),     "%9.1f"],
                                 [pg_z_offset_sigmas,      all_refls_count.as_double(), "%9.1f"],
                                 [pg_offset_sigmas,        all_refls_count.as_double(), "%9.1f"]]:

        r2.append("")
        if data is None and weights is None:
          r1.append("")
          continue
        stats = flex.mean_and_variance(data, weights)
        r1.append(fmt%stats.mean())

      r1.append("")
      r2.append("%6.1f"%flex.mean(all_refls_count.as_double()))
      detector_table_data.append(r1)
      detector_table_data.append(r2)

    print("Detector statistics, I.E. measurements of parameters relative to the detector plane:")
    print(table_utils.format(detector_table_data,has_header=3,justify='center',delim=" "))

    print("PanelG Id: panel group id or panel id, depending on hierarchy_level. For each panel group, weighted means and weighted standard deviations (Sigmas) for the properties listed below are computed using the matching panel groups between the input experiments.")
    print("Dist: distance from center of panel group to the beam center")
    print("Dist Sigma: weighted standard deviation of the measurements used to compute Dist")
    print("Normal Angle: angle between the normal vector of the detector at its root hierarchy level and the normal of the panel group")
    print("RNormal Angle: radial component of Normal Angle")
    print("TNormal Angle: transverse component of Normal Angle")
    print("RotZ: deviation from 90 degrees of the rotation of each panel group around the detector normal")
    print("F Offset: offset of panel group along the detector's fast axis")
    print("S Offset: offset of panel group along the detector's slow axis")
    print("Z Offset: offset of panel group along the detector normal")
    print("Offset: offset of panel group in F,S,Z space. Sigma is F, S, Z offset sigmas summed in quadrature.")
    print("N refls: number of reflections summed between both matching panel groups. This number is used as a weight when computing means and standard deviations.")
    print("All: weighted mean of the values shown")
    print()
    print("Sigmas in this table are computed using the standard deviation of 2 measurements (I.E. a panel's Z Offset is measured twice, once in each input dataset). This is related by a factor of sqrt(2)/2 to the mean of the Delta Z parameter in the congruence statistics table above, which is the difference between Z parameters.")
    print()

    row = ["Overall"]
    for refls in reflections:
      row.append("%6.1f"%(math.sqrt(flex.sum_sq(refls['difference_vector_norms'])/len(refls))*1000))
      row.append("%6.1f"%(math.sqrt(flex.sum_sq(refls['radial_displacements'])/len(refls))*1000))
      row.append("%6.1f"%(math.sqrt(flex.sum_sq(refls['transverse_displacements'])/len(refls))*1000))
      row.append("%8d"%len(refls))
    rmsds_table_data.append(row)

    print("RMSDs by detector number")
    print(table_utils.format(rmsds_table_data,has_header=3,justify='center',delim=" "))
    print("PanelG Id: panel group id or panel id, depending on hierarchy_level")
    print("RMSD: root mean squared deviation between observed and predicted spot locations")
    print("rRMSD: RMSD of radial components of the observed-predicted vectors")
    print("tRMSD: RMSD of transverse components of the observed-predicted vectors")
    print("N refls: number of reflections")

    # Show stats for detector hierarchy root
    def _print_vector(v):
      for i in v:
        print("%10.5f"%i, end=' ')
      print()
    for d_id, d in enumerate(detectors):
      ori = d.hierarchy().get_origin()
      norm = d.hierarchy().get_normal()
      fast = d.hierarchy().get_fast_axis()
      slow = d.hierarchy().get_slow_axis()
      print("Detector", d_id, "origin:   ", end=' '); _print_vector(ori)
      print("Detector", d_id, "normal:   ", end=' '); _print_vector(norm)
      print("Detector", d_id, "fast axis:", end=' '); _print_vector(fast)
      print("Detector", d_id, "slow axis:", end=' '); _print_vector(slow)

    # Unit cell statstics
    lengths = flex.vec3_double()
    angles = flex.vec3_double()
    weights = flex.double()
    for refls, expts in zip(reflections, [d.data for d in params.input.experiments]):
      for crystal_id, crystal in enumerate(expts.crystals()):
        lengths.append(crystal.get_unit_cell().parameters()[0:3])
        angles.append(crystal.get_unit_cell().parameters()[3:6])
        weights.append(len(refls.select(refls['id'] == crystal_id)))

    print("Unit cell stats (angstroms and degrees), weighted means and standard deviations")
    for subset, tags in zip([lengths, angles], [["Cell a", "Cell b", "Cell c"],["Cell alpha", "Cell beta", "Cell gamma"]]):
      for data, tag in zip(subset.parts(), tags):
        stats = flex.mean_and_variance(data, weights)
        print("%s %5.1f +/- %6.3f"%(tag, stats.mean(), stats.gsl_stats_wsd()))

    if params.tag is None:
      tag = ""
    else:
      tag = "%s "%params.tag

    if params.show_plots:
      # Plot the results
      detector_plot_dict(self.params, detectors[0], refl_counts, u"%sN reflections"%tag, u"%6d", show=False)
      #detector_plot_dict(self.params, detectors[0], delta_normals, u"%sAngle between normal vectors (\N{DEGREE SIGN})"%tag, u"%.2f\N{DEGREE SIGN}", show=False)
      detector_plot_dict(self.params, detectors[0], z_angles, u"%sZ rotation angle between panels (\N{DEGREE SIGN})"%tag, u"%.2f\N{DEGREE SIGN}", show=False)
      detector_plot_dict(self.params, detectors[0], f_deltas, u"%sFast displacements between panels (microns)"%tag, u"%4.1f", show=False)
      detector_plot_dict(self.params, detectors[0], s_deltas, u"%sSlow displacements between panels (microns)"%tag, u"%4.1f", show=False)
      detector_plot_dict(self.params, detectors[0], z_offsets_d, u"%sZ offsets along detector normal (microns)"%tag, u"%4.1f", show=False)
      detector_plot_dict(self.params, detectors[0], z_deltas, u"%sZ displacements between panels (microns)"%tag, u"%4.1f", show=False)
      detector_plot_dict(self.params, detectors[0], o_deltas, u"%sOverall displacements between panels (microns)"%tag, u"%4.1f", show=False)
      plt.show()

if __name__ == '__main__':
  with show_mail_on_error():
    script = Script()
    script.run()


import os

def getPaths():
    PREFIX = os.environ['PROTEUS_PREFIX']
    INCDIR = PREFIX+'/'+'include'
    LIBDIR = PREFIX+'/'+'lib'

    # grib_api paths
    grib_api_keys = ['grib_api_dir', '# grib_api_incdir','# grib_api_libdir']
    grib_api_dirs = [PREFIX, INCDIR, LIBDIR]
    grib_api_dict = {}
    index = 0
    for key in grib_api_keys:
        grib_api_dict[key] = grib_api_dirs[index]
        index+=1

    # jasper paths
    jasper_keys = ['jasper_dir', '# jasper_incdir', '# jasper_libdir']
    jasper_dirs = [PREFIX, INCDIR, LIBDIR]
    jasper_dict = {}
    index = 0
    for key in jasper_keys:
        jasper_dict[key] = jasper_dirs[index]
        index+=1

    return grib_api_dict, jasper_dict

def generate_setup_cfg(grib_api_dict, jasper_dict):
    """ Initialize input/out for setup.cfg file"""
    f = open('setup.cfg', 'w')

    output = {}
    comments = ['# grib_api directories path',
                '# jasper directories path']

    output['directories'] = '[directories]'

    # Directories
    f.write(output['directories']+'\n')
    f.write(comments[0]+'\n')
    for key in grib_api_dict.keys():
        dirVariable = key+'='+grib_api_dict[key]
        f.write(dirVariable+'\n')

    f.write(comments[1]+'\n')
    for key in jasper_dict.keys():
        dirVariable = key+'='+jasper_dict[key]
        f.write(dirVariable+'\n')

    f.close()

if __name__=='__main__':
    """ Script that generates a setup.cfg file which will
        read system variable PROTEUS_PREFIX and write in
        the necessary include and library directories for
        grib_api and jasper. """

    [grib_api_dict, jasper_dict] = getPaths()

    generate_setup_cfg(grib_api_dict, jasper_dict)


from selenium import webdriver
from selenium.webdriver.common.keys import keys
import unittest

browser = webdriver.Firefox()
browser.get('http://localhost:8000')

class NewVisitorTest(unittest.TestCase):

    def setUp(self):
        self.browser = webdriver.Firefox()

    def tearDown(self):
        self.browser.quit()

    # def test_can_start_a_post(self):
    #     self.browser.get('http://localhost:8000')

    # I am on the website. I notice the name "Cool Blog" in the title of the siteself.
        self.assertIn('Cool Blog', header_text)

    # There is a 'title' section, and a 'content' section; where I am allowed to name  a post
        titlebox = self.browser.find_element_by_id('id_new_post_title')
        inputbox = self.browser.find_element_by_id('id_new_post_content')

    # I write "My bad day" in the title of the postself. I hit enter,
        titlebox.sendKeys("My Bad Day")
    # I then write "My day was bad because I was hit by a truck" in the content section of the site.
        inputbox.sendKeys("My day was bad because I was hit by a truck")

    # I hit a button called "Save", which adds the post; title and all, to the bottom of the page
        submitbutton = self.browser.find_element_by_id('submit_button')
    # The input box for the "title" and "content" are now blank.

    # I refresh the page, the content is still thereself.

if __name__ == '__main__':
    unittest.main(warnings='ignore')


pa_list = open("./paList.txt", "r").read().split()
ag_list = open('./agList.txt', 'r').read().split()

import random
def calc_best_remaining(possible_words, remaining_words, printable=False, need_percentage=False, future_guesses=1):
    words_score = {}
    percentage_score = {}
    best_score = 100
    for x in possible_words:
        score_dict = calc_score_dict(x, remaining_words)

        # while future_guesses != 1:
        #     future_guesses -= 1
        #     for score in score_dict:
        #         for word in possible_words:
        #             new_score_dict = calc_score_dict(word, score_dict[score])

        final_score = 0
        for l in score_dict.values():
            final_score += len(l)**2
        final_score /= len(remaining_words)
        if final_score < best_score:
            best_score = final_score
        if final_score == best_score or set_mode == 'full_rank':
            words_score[x] = final_score
    if need_percentage:
        for k in words_score:
            percentage_score[k] = best_score/words_score[k]*100
    if printable:
        print(possible_words, words_score, score_dict)
    final = dict(sorted(words_score.items(), key=lambda item: item[1]))
    # if two_words == 0:
    if need_percentage:
        return final, percentage_score
    else:
        return final
    # else:
    #     p = 0
    #     words_score = {}
    #     percentage_score = {}
    #     best_score = 100
    #     calculated_nums = 0
    #     for f in final.keys(): # ['words']
    #         calculated_nums += 1
    #         print(f, calculated_nums)
    #         for x in possible_words:
    #             final_score = 0
    #             score_dict = {}
    #             for y in remaining_words:
    #                 score_list = [['0', '0'], ['0', '0'], ['0', '0'], ['0', '0'], ['0', '0']]
    #                 for a, b, c, n in zip(f, x, y, range(5)):
    #                     if a == c:
    #                         score_list[n][0] = '2'
    #                     elif a in y:
    #                         score_list[n][0] = '1'
    #                     if b == c:
    #                         score_list[n][1] = '2'
    #                     elif b in y:
    #                         score_list[n][1] = '1'
    #                 score = []
    #                 for i in score_list:
    #                     for j in i:
    #                         score.append(j)
    #                 score = ''.join(score)
    #                 if score not in score_dict:
    #                     score_dict[score] = 1
    #                 else:
    #                     score_dict[score] += 1
    #             for l in score_dict.values():
    #                 final_score += l**2
    #             final_score /= sum(score_dict.values())
    #             if final_score < best_score:
    #                 best_score = final_score
    #             words_score[f+'-'+x] = final_score
    #         if f in possible_words:
    #             possible_words.remove(f)
    #         p += 1
    #         if p == two_words:
    #             break
    #     for k in words_score:
    #         percentage_score[k] = best_score / words_score[k] * 100
    #     if printable:
    #         print(possible_words, words_score, score_dict)
    #     final = dict(sorted(words_score.items(), key=lambda item: item[1]))
    #     while len(final) > 1000:
    #         final.popitem()
    #     return final, percentage_score

def calc_score_dict(word, remaining_words):
    score_dict = {}
    for y in remaining_words:
        score = calc_score(word, y)
        score = ''.join(score)
        if score not in score_dict:
            score_dict[score] = [y]
        else:
            score_dict[score] += [y]
    return score_dict

def calc_score(word, answer):
    yellow_letters = calc_yellow_and_green(word, answer)
    score = ['0', '0', '0', '0', '0']  # ['0','0','0','0','0']
    for a, b, c in zip(word, answer, range(5)):
        if a == b:
            score[c] = '2'
        elif a in answer and a in yellow_letters:
            score[c] = '1'
            yellow_letters.remove(a)
    return score

def calc_yellow_and_green(word, answer, need_green=False):
    green_letters = []
    yellow_letters = []
    for a, b in zip(word, answer):
        if a == b:
            green_letters.append(a)
            if green_letters.count(a) + yellow_letters.count(a) > answer.count(a):
                yellow_letters.remove(a)
        elif a in answer:
            if green_letters.count(a) + yellow_letters.count(a) < answer.count(a):
                yellow_letters.append(a)
    if need_green:
        return yellow_letters, green_letters
    else:
        return yellow_letters

def FullRank():
    remaining_words = pa_list+ag_list
    final, percentage_score = calc_best_remaining(remaining_words, pa_list, need_percentage=True)
    ranking = 0
    for k in final:
        ranking += 1
        print(ranking, k, round(final[k], 3), round(percentage_score[k], 2))

def CalcGame(): # 00010, 00020, 00000
    solo_word = True # Game config
    hard_mode = False # Game config
    answers = []
    if solo_word:
        answers.append(random.choice(pa_list))
        # answers = ['mourn'] # Game config
    else:
        answers = pa_list
        shit_dict = {}
        attempts_score = {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0, '6': 0}
    for answer in answers:
        if solo_word:
            print("Answer:", answer)
        attempts = 1
        words_score = {}
        crt_list = pa_list
        starting_words = ['march'] # Game config
        start_word = 1
        last_word = starting_words[0]
        game_end = False
        while not game_end:
            score = calc_score(last_word, answer)
            remaining_words = []
            for k in crt_list:
                valid = True
                for a, b, c in zip(last_word, k, score):
                    if c == '0' and (a == b or k.count(a) >= last_word.count(a)) or c == '1' and (a not in k or a == b) or c == '2' and a != b: #k.count(a) >= last_word.count(a)  or a not in answer and a in k
                        valid = False
                        break
                if valid:
                    remaining_words.append(k)
            if solo_word:
                print(last_word, len(remaining_words), remaining_words)
            if hard_mode:
                all_words = remaining_words
            else:
                all_words = pa_list+ag_list
            if len(starting_words) > start_word:
                last_word = starting_words[start_word]
                start_word += 1
            elif len(remaining_words) > 2:
                final = calc_best_remaining(all_words, remaining_words)
                if solo_word and hard_mode:
                    print(final)
                for k in final:
                    last_word = k
                    best_score = final[k]
                    break
                for k in final:
                    if final[k] == best_score:
                        if k in remaining_words:
                            last_word = k
                            break
                    else:
                        break
            else:
                last_word = remaining_words[0]
            attempts += 1
            if last_word == answer:
                game_end = True
            else:
                crt_list = remaining_words
        if not solo_word:
            print(answer, attempts)
            if attempts < 7:
                attempts_score[str(attempts)] += 1
            else:
                shit_dict[last_word] = attempts
    if solo_word:
        print(last_word, answer, attempts)
    else:
        print(attempts_score)
        print(shit_dict)
        average_attempts = 0
        for k, l in zip(attempts_score.keys(), attempts_score.values()):
            average_attempts += (l/sum(attempts_score.values())) * int(k) #l*k/sum(attempts_score.values())
        print(round(average_attempts, 4))

def GetInput():
    # import time
    from termcolor import cprint
    random_first_word = False # Game config
    answer = random.choice(pa_list)
    attempts = 6
    game_end = False
    print('')
    print('\t', attempts, '| - - - - -', end=' ')
    while not game_end and attempts != 0:
        if not random_first_word or attempts != 6:
            player_word = input('| guess: ')
        else:
            player_word = random.choice(pa_list+ag_list)
            print('| guess:', player_word)
        if player_word in pa_list+ag_list:
            attempts -= 1
            print('\t', attempts, end=' | ')
            yellow_letters = calc_yellow_and_green(player_word, answer)
            for a, b in zip(player_word, answer):
                if a == b:
                    cprint(a, 'green', end=' ')
                elif a in answer and a in yellow_letters:
                    cprint(a, 'yellow', end=' ')
                    yellow_letters.remove(a)
                else:
                    print(a, end=' ')
            if player_word == answer:
                game_end = True
        else:
            print('\t', attempts, '| - - - - -', end=' ')
    if game_end:
        cprint('\n\n\tYou won!', 'green')
    else:
        cprint('\n\n\tYou lost!', 'red')
        print('\tAnswer: ', end='')
        cprint(answer, 'green')

def GetCheat():
    answer = 'plate' # Game Config
    anagrams = {}
    # second_chance = False
    while len(anagrams) == 0:
        for word in pa_list+ag_list:
            yellow_letters, green_letters = calc_yellow_and_green(word, answer, need_green=True)
            if len(yellow_letters)+len(green_letters) >= 4 and len(yellow_letters)+len(green_letters) != 5:
                if str(len(yellow_letters))+'y'+str(len(green_letters))+'g' not in anagrams:
                    anagrams[str(len(yellow_letters))+'y'+str(len(green_letters))+'g'] = [word]
                else:
                    anagrams[str(len(yellow_letters))+'y'+str(len(green_letters))+'g'] += [word]
    for i in anagrams:
        print(i, len(anagrams[i]), anagrams[i])

modes = ['input', 'calc_game', 'cheat', 'full_rank']
set_mode = modes[0]
if set_mode == 'full_rank':
    FullRank()
elif set_mode == 'calc_game':
    CalcGame()
elif set_mode == 'input':
    while True:
        GetInput()
        if input('\n\tPlay Again? [Y] [N] ').upper() != 'Y':
            break
elif set_mode == 'cheat':
    GetCheat()


'''
    인코딩, 디코딩 : 서로 다른 형식 간의 데이터 변환 시, 사용되는 프로세스
    인코딩 : 텍스트 또는 문자열 등을 원래 형식으로(ex. 유니코드, utf-8, utf-16..) 특정코드로 지정하는 것
    디코딩 : 특정코드로 지정한 것을 원래 문자열 또는 텍스트로 되돌리는 것(인코딩된 바이트를 다시 문자열로 변환된다.)
    *인코딩의 선택은 응용프로그램의 요구 사항과 지원되는 문자집합에 따라 다르다.
        문자열 -> 인코딩 [특정코드 변환] -> 디코딩 [복원] -> 문자열
'''
import base64   #python의 표준 인코드 모듈

if __name__ == '__main__':
    #문자열 -> 인코딩 [특정코드 변환] -> 디코딩 [복원] -> 문자열
    #system
    text='hello python!'
    encoding_text = text.encode('utf-8')    #b'hello python!' : 인코딩된 문자열 (defaultencoding : utf-8)
                                            # => 이렇게 하면 utf-8/16인지 모름. defaultencoding이 다르다면 다시 변환해줘야함
    print(encoding_text)
    decoding_text = encoding_text.decode('utf-8')
    print(decoding_text)

    #python 프로그램
    o_text = 'hello python!'
    en_text = base64.b64encode(o_text.encode('utf-8'))  #바이트 문자열로 인코딩
    print(en_text)

    de_text=base64.b64decode(en_text).decode('utf-8')   #원본으로 디코딩
    print(de_text)
    '''
    base64 : 1) 바이너리 데이터를 ASCII문자로 인코딩하는데 사용된다
             2) 이미지, 오디오, 영상 파일 또는 바이너리 문서와 같은 데이터를 처리하는 용도로 사용한다.
             3) 3바이트 이진을 4개의 ASCII로 관리하는 것
                --> (이진데이터 -> 24비트 시퀀스 -> 4개의 6비트 청크 분할 -> 6비트 10진수 ASCII)
             4) 이메일(첨부파일 인코딩), http통신 (Json 페이로드 = 이미지 혹은 이진파일), 암호화
    '''


import asyncio
from flask import (
    Blueprint,
    render_template,
)

# local imports
from fetch_data import fetch_data_api
from validate_request import validate_latlon
from postprocessing import postprocess
from config import GS_BASE_URL, WEST_BBOX, EAST_BBOX
from . import routes

geology_api = Blueprint("geology_api", __name__)

wms_targets = []
wfs_targets = {"detailed_geologic_units": "STATE_UNIT,AGE_RANGE"}

def package_usgsgeol(geol_resp):
    """Package geology data in dict"""
    title = "USGS Geologic Map of Alaska"
    if geol_resp[0]["features"] == []:
        return None
    di = {}
    gunit = geol_resp[0]["features"][0]["properties"]["STATE_UNIT"]
    age = geol_resp[0]["features"][0]["properties"]["AGE_RANGE"]
    di.update({"title": title, "name": gunit, "age": age})
    return di

@routes.route("/geology/")
@routes.route("/geology/abstract/")
@routes.route("/geology/point/")
def geo_about():
    return render_template("documentation/geology.html")

@routes.route("/geology/point/<lat>/<lon>")
def run_fetch_geology(lat, lon):
    """Run the async requesting and return data
    example request: http://localhost:5000/geology/60.606/-143.345
    """
    validation = validate_latlon(lat, lon)
    if validation == 400:
        return render_template("400/bad_request.html"), 400
    if validation == 422:
        return (
            render_template(
                "422/invalid_latlon.html", west_bbox=WEST_BBOX, east_bbox=EAST_BBOX
            ),
            422,
        )
    # verify that lat/lon are present
    try:
        results = asyncio.run(
            fetch_data_api(GS_BASE_URL, "geology", wms_targets, wfs_targets, lat, lon)
        )
    except Exception as exc:
        if hasattr(exc, "status") and exc.status == 404:
            return render_template("404/no_data.html"), 404
        return render_template("500/server_error.html"), 500
    data = package_usgsgeol(results)
    return postprocess(data, "geology")


import logging

# create logger
log_level = logging.DEBUG  # logging.DEBUG
logging.basicConfig(level=log_level)
logger = logging.getLogger('postproc.py')
logger.setLevel(level=log_level)

def source_result_postproc(replacer_cpp, node, eq_terms_names=["=", "=-"]):

    '''Replace source[delay] or source in eq.lhs to result.
    Ex: source[delay][idx+0]|->result[idx+0]'''

    if replacer_cpp.get_node_type(node) in eq_terms_names:

        successors = replacer_cpp.get_successors(node)

        left_node = successors[1]

        left_node_out = replacer_cpp.get_output_out(left_node)

        if left_node_out is None:
            logger.debug("source_result_postproc eq has no cpp output")
            return
        if 'source[delay]' in left_node_out:
            new_out = (left_node_out
                       .replace('source[delay]', 'result'))
        elif 'source' in left_node_out:
            new_out = (left_node_out
                       .replace('source', 'result'))
        else:
            logger.debug("source_result_postproc eq has nothing for"
                         + " source-result conversion")
            return
        replacer_cpp.set_output_out(left_node, new_out)

def delay_postproc(replacers, nodes_lists):

    '''Convert delays for all term's that have it.
    node have or have not global_data['delay_data'] value.
    If have, it will be transformed into cpp corect
    order (for ex: [5.1, 1.5, 2.7]->[3, 1, 2])

    First it collect delays (and remember from whom)
    then convert them,
    finely replace them into it's term.cpp.out string.

    var (like U(t-1.1)) used for factorize delays. In
    that case used only var[0] (U).

    Inputs:

    - ``replacers`` -- replacer for each equation.

    - ``nodes_lists`` -- list of nodes for each equation.
    count of that list must be equal count of equations
    (and => count of replacers)

    Examples:
    for node:
       term.cpp.global_data['delay_data'] = (1, 'V(t-1.1)', '1.1')
       term.cpp.out = source[delay]
    return:
       term.cpp.out_new = source[coverted delay for 1.1]

    Return:
    (in sent U*U(t-3.1)*V(t-3.3)*U(t-1.3)*V(t-1.1) ...)

    {'converted_delay': 2, 'delay_data': (1, 'U(t-3.1)', '3.1')}
    for U(t-3.1) where:
    converted_delay : source[converted_delay][0]
    delay_data[0] - number of delay's term U(t-3.1)
                    in all delays terms.

    {'converted_delay': 2, 'delay_data': (2, 'V(t-3.3)', '3.3')}
    for V(t-3.3) where:
    converted_delay : source[converted_delay][1]
    delay_data[0] - number of delay's term V(t-3.3)
                    in all delays terms.

    {'converted_delay': 1, 'delay_data': (3, 'U(t-1.3)', '1.3')}
    for U(t-1.3) where:
    converted_delay : source[converted_delay][0]
    delay_data[0] - number of delay's term U(t-1.3)
                    in all delays terms.

    Update:
    It now use term instead of term_id for term identification
    in res[var][delay] = [term] and in map_td.
    '''

    def convert_delays(delays):

        '''Convert float delay to int,
        according it's value.

        For ex:
        [5.1, 1.5, 2.7]->[3, 1, 2]
        '''
        # delays = copy(delays)
        delays.sort()
        sdelays = [delays.index(val)+1 for val in delays]
        logger.debug(sdelays)
        return(zip(delays, sdelays))

    logger.debug("FROM postproc")

    # FOR factorize terms delays for var:
    # res[val] = [(delay_0, term_id_0), ...]
    res = {}
    for i, nodes_list in enumerate(nodes_lists):
        for j, term in enumerate(nodes_list):
            '''
            if type(term) == str:
                continue
            '''
            try:
                term_data = replacers[i].get_output_data(term)
                if term_data is None:
                    continue

                delay_data = term_data["delay_data"]
                # delay_data = term.output.cpp.global_data['delay_data']
                term_id, var, delay = delay_data
                # U(t-1.1)->U
                var = var[0]
                replacer_node_id = (i, j)
                if var in res.keys():
                    if delay in res[var].keys():
                        res[var][delay].append(replacer_node_id)  # term_id
                    else:
                        res[var][delay] = [replacer_node_id]  # term_id
                else:
                    res[var] = {delay: [replacer_node_id]}  # term_id
            except KeyError:
                pass
            except AttributeError:
                # if term not have out:
                # (like +):
                continue
    logger.debug("res")
    logger.debug(res)
    # END FOR

    # FOR map float delays to it's source equivalent:
    # map_dsd: var -> delay, sdelay
    map_dsd = lambda var: convert_delays(list(res[var].keys()))

    # map_td: term_id -> source delay
    map_td = dict([(equal_term, sdelay) for var in res.keys()
                   for delay, sdelay in map_dsd(var)
                   for equal_term in res[var][delay]])
    logger.debug("map_td")
    logger.debug(map_td)
    # END FOR

    # FOR find terms for converted delay:
    out_new = []
    for i, nodes_list in enumerate(nodes_lists):
        for j, term in enumerate(nodes_list):
            try:
                '''
                if type(term) == str:
                    out_new.append(term)
                    continue
                '''
                term_data = replacers[i].get_output_data(term)
                if term_data is None:
                    continue

                delay_data = term_data["delay_data"]
                # delay_data = term.output.cpp.global_data['delay_data']

                # if delay
                # transform source[delay]->source[1]:
                term_id, var, delay = delay_data
                sdelay = map_td[(i, j)]
                # sdelay = map_td[term]  # term_id
                term_out = replacers[i].get_output_out(term)
                if term_out is None:
                    continue
                if 'delay' not in term_out:
                    continue
                replacers[i].set_output_out(term,
                                            term_out.replace('delay',
                                                             str(sdelay)))
                replacers[i].set_output_data(term, 'converted_delay', sdelay)
                '''
                term.output.cpp.out = (term.output
                                       .cpp.out.replace('delay', str(sdelay)))
                term.output.cpp.global_data['converted_delay'] = sdelay
                '''
                out_new.append(term)
            except KeyError:
                # in sdelay = map_td[term]
                try:
                    # if no delay
                    # transform source[delay]->source[0]
                    term_out = replacers[i].get_output_out(term)
                    if term_out is None:
                        continue
                    if 'delay' not in term_out:
                        continue
                    replacers[i].set_output_out(term, term_out.replace('delay',
                                                                       str(0)))
                    '''
                    term.output.cpp.out = (term.output.
                                           cpp.out.replace('delay', str(0)))
                    '''
                    out_new.append(term)
                except:
                    pass
            except AttributeError:
                # if term not have out:
                # (like +):
                out_new.append(term)
    # logger.debug([o.out for o in out_new if type(o) != str])
    # return(out)  # out_new


import sys
import os
# Get the current directory of the file
current_directory = os.path.dirname(os.path.abspath(__file__))

# Navigate to the parent directory
parent_directory = os.path.dirname(current_directory)

# Define the relative path to the 'RefactorCode' folder
relative_path = 'refactorcode'

# Construct the absolute path to the 'RefactorCode' folder
absolute_path = os.path.join(parent_directory, relative_path)

# Append the absolute path to sys.path
sys.path.append(absolute_path)
from assignment import DoublyLinkedList, twenties, create_linked_list, Node

def test_node():
    dL = DoublyLinkedList()

    for i in range(4):
         dL.add_last(Node(i))

    print(dL)
    assert str(dL) == f"(0, (1, (2, (3, ('Trailer', None)))))"

def test_size():
    dL = create_linked_list()
    print(dL.size())
    assert dL.size() == 4

def test_empty():
    dL = create_linked_list()
    dL2 = DoublyLinkedList()
    print(dL.is_empty())

    dL2 = DoublyLinkedList()
    print(dL2.is_empty())

    assert dL.is_empty() == False
    assert dL2.is_empty() == True
    del dL2

def test_get_element():
    dL = create_linked_list()
    #Test your implementation here
    print(dL.get_first())
    print(dL.get_last())

    assert str(dL.get_first()) == f"(0, (1, (2, (3, ('Trailer', None)))))"
    assert str(dL.get_last()) == f"(3, ('Trailer', None))"

def test_previous():
    dL = create_linked_list()
#Test your implementation here
    print(dL.get_last().get_previous())
    print(dL.get_first().get_next())

    assert str(dL.get_last().get_previous()) == "(2, (3, ('Trailer', None)))"
    assert str(dL.get_first().get_next()) == "(1, (2, (3, ('Trailer', None))))"

def test_add_before_after():
    dL = create_linked_list()
    #Test your implementation here
    dL.add_after(Node(42),dL.get_first())
    dL.add_before(Node(34),dL.get_last())

    print(dL)
    assert str(dL) == "(0, (42, (1, (2, (34, (3, ('Trailer', None)))))))"

def test_add_first_last():
    dL = create_linked_list()
    dL.add_after(Node(42),dL.get_first())
    dL.add_before(Node(34),dL.get_last())
    #Test your implementation here
    dL.add_first(Node(7))
    dL.add_last(Node(-1))
    print(dL)
    assert str(dL) == "(7, (0, (42, (1, (2, (34, (3, (-1, ('Trailer', None)))))))))"

def test_remove():
    dL = create_linked_list()
    dL.add_after(Node(42),dL.get_first())
    dL.add_before(Node(34),dL.get_last())
    #Test your implementation here
    dL.add_first(Node(7))
    dL.add_last(Node(-1))
    #Test your implementation here
    dL.remove(dL.get_first())
    print(dL.get_first())

    assert dL.get_first().get_element() == 0

def test_map():
    dL = create_linked_list()
    dL = create_linked_list()
    dL.add_after(Node(42),dL.get_first())
    dL.add_before(Node(34),dL.get_last())
    #Test your implementation here
    dL.add_first(Node(7))
    dL.add_last(Node(-1))
    dL.remove(dL.get_first())
    #Test your implementation here
    dL.map(lambda x: x**2)

    print(dL)

    assert str(dL) == "(0, (1764, (1, (4, (1156, (9, (1, ('Trailer', None))))))))"

#Dont know what dn is and dont want to figure it out
# def test_iterator():
#     dL = create_linked_list()
#     for node in dL:
#         print(node.get_element() == dn.__next__().get_element())
#         print(dn)

def test_twenties():
    doubleDigits = twenties()

    doubleDigits.map(lambda x: x+10)

    print(doubleDigits)
    assert str(doubleDigits) =="(30, (31, (32, (33, (34, (35, (36, (37, (38, (39, ('Trailer', None)))))))))))"


import random

DIGITS_NUM = 11 #this is the number of digits for guessing
MAX_NUMBER_ATTEMPTS = 10 #this is the number of attempts to guess the number

def main():
    print(''' Bagels, a deductive logic game.
    by Al Sweigart al@inventwithpython.com

    I am thinking of a {}-digit number with no repeated digits.
    Try to guess what it is. Here are some clues:
    When I say:     That means:
      Pico          One digit is correct but in the wrong position
      Fermi         One digit is correct and in the right position
      Bagels        No digit is correct '''.format(DIGITS_NUM))

    while True: #Main game loop
        # This socres the secret number the player needs to guess:
        secretNum = getSecretNum()
        print('I have though up a number.')
        print(' You have {} guesses to get it.'.format(MAX_NUMBER_ATTEMPTS))

        numGuesses = 1
        while numGuesses <= MAX_NUMBER_ATTEMPTS:
            guess = ''
            #keep looping until they enter a valid guess:
            while len(guess) != DIGITS_NUM or not guess.isdecimal():
                print('Guess #{}: '.format(numGuesses))
                guess = input('> ')
            clues = getClues(guess, secretNum)
            print(clues)
            numGuesses += 1

            if guess == secretNum:
                break # They're correct, so break out of this loop
            if numGuesses > MAX_NUMBER_ATTEMPTS:
                print('You ran out of guesses.')
                print('The answer was {}.'.format(secretNum))
        #Ask the player if they want to play again.
        print('Do you want to play again? (yes or no)')
        if not input('> ').lower().startswith('y'):
            break
    print('Thank you for Playing')

def getSecretNum():
    """Returns a string made of a DIGITS_NUM unique random digits."""
    numbers = list('0123456789') # create a list of digits 0 to 9
    random.shuffle(numbers) # shuffles the number into a random order.

    #get the first DIGITS_NUM digits in the list for the secret number:
    secretNum = ''
    for i in range(DIGITS_NUM):
        secretNum += str(numbers[i])
    return secretNum

def getClues(guess, secretNum):
    """Returns a string with the pico, fermi, bagels clues for a guess
    and secret number pair."""
    if guess == secretNum:
        return 'You got it!'

    clues = []

    for i in range(len(guess)):
        if guess[i] == secretNum[i]:
            # A correct digit in the correct place
            clues.append('Fermi')
        elif guess[i] in secretNum:
            # a correct digit in the wrong place
            clues.append('Pico')
    if len(clues) == 0:
        return 'Bagels'
    else:
        clues.sort()
        return ' '.join(clues)

if __name__ == '__main__':
    main()


import os
import sys
import yaml

settings = False
cwd = os.getcwd()
path = cwd + '/.settings.yaml'
assert os.path.isfile(path)
with open(path, 'r') as f:
    settings = yaml.load(f)

def getSettings():
    return settings

def getServicesFromGroups(groups):
    ret_services = []
    for group in groups:
        if group in settings['groups']:
            if settings['groups'][group] == 'all':
                return list(settings['services'].keys())
            for service in settings['groups'][group]:
                if service not in ret_services:
                    ret_services.append(service)
    if len(ret_services) < 1:
        if 'default' not in groups:
            return getServicesFromGroups(['default'])
    return ret_services


from __future__ import absolute_import, division, print_function
import sys
from iotbx.pdb import secondary_structure as ss
from libtbx.utils import Sorry, format_cpu_times
from libtbx import test_utils

ann_1 = """\
SHEET    1   B 4 ARG A  90  PHE A  95  0
SHEET    2   B 4 GLY A 179  ARG A 184 -1  N  GLY A 179   O  PHE A 95
SHEET    3   B 4 ARG A 128  THR A 132 -1  N  ALA A 130   O  ARG A 184
SHEET    4   B 4 VAL A 148  PRO A 152  0  O  ILE A 151   N  VAL A 129
"""

ann_2 = """\
SHEET    1   B 4 ARG A  90  PHE A  95  0
SHEET    2   B 4 GLY A 179  ARG A 184 -1  N  GLY A 179   O  PHE A 95
SHEET    3   B 4 ARG A 128  THR A 132 -1  N  ALA A 130   O  ARG A 184
SHEET    4   B 4 VAL A 148  PRO A 152 -1  O  ILE A 151   N  VAL A 129
"""

ann_3 = """\
SHEET    1   3 3 MET A 157  VAL A 161  0
SHEET    2   3 3 ILE A 114  ALA A 122 -1
SHEET    3   3 3 THR A 193  LEU A 201  1
"""

def exercise_01():
  try:
    annot = ss.annotation.from_records(records=ann_1.split('\n'))
  except Sorry as e:
    m = str(e)
    assert m.find("should be 1 or -1 for non-first strand") > 0

def exercise_02():
  annot = ss.annotation.from_records(records=ann_2.split('\n'))
  assert len(annot.helices) == 0
  assert len(annot.sheets) == 1
  sheet = annot.sheets[0]
  assert sheet.n_strands == 4
  assert len(sheet.strands) == 4
  assert len(sheet.registrations) == 4
  assert sheet.registrations[0] is None

def exercise_03():
  answer_phil_str = """\

protein.sheet {
  sheet_id = "  3"
  first_strand = chain 'A' and resid  157  through  161
  strand {
    selection = chain 'A' and resid  114  through  122
    sense = antiparallel
    bond_start_current = None
    bond_start_previous = None
  }
  strand {
    selection = chain 'A' and resid  193  through  201
    sense = parallel
    bond_start_current = None
    bond_start_previous = None
  }
}"""
  annot = ss.annotation.from_records(records=ann_3.split('\n'))
  assert len(annot.helices) == 0
  assert len(annot.sheets) == 1
  ann_sheet = annot.sheets[0]
  assert ann_sheet.n_strands == 3
  assert ann_sheet.registrations == [None]*3
  only_sheet = annot.sheets[0]
  phil_string = annot.as_restraint_groups()
  assert not test_utils.show_diff(phil_string, answer_phil_str,
      strip_trailing_whitespace=True)

def exercise(args):
  exercise_01()
  exercise_02()
  exercise_03()
  print("OK")
  print(format_cpu_times())

if (__name__ == "__main__"):
  exercise(sys.argv[1:])


import string

def load_doc(filename):
    with open(filename, 'r') as f:
        text = f.read()
    return text

def load_descriptions(doc):
    mapping = {}
    for line in doc.split('\n'):
        tokens = line.split()
        if len(line) < 2:
            continue
        image_id, text = tokens[0], tokens[1:]
        image_id = image_id.split('.')[0]
        text = ' '.join(text)
        mapping.setdefault(image_id, []).append(text)
    return mapping

def clean_description(texts):
    puncts = str.maketrans('', '', string.punctuation)
    vocabs = set()
    for key, text_list in texts.items():
        for i, cur in enumerate(text_list):
            tokens = cur.split()
            tokens = [w.lower() for w in tokens]
            tokens = [w.translate(puncts) for w in tokens]
            # remove haning 's' and 'a'
            tokens = [w for w in tokens if len(w)>1]
            tokens = [w for w in tokens if w.isalpha()]
            vocabs.update(tokens)
            text_list[i] = ' '.join(tokens)
    return list(vocabs)

def save_texts(texts, filename):
    with open(filename, 'w') as f:
        for key, text_list in texts.items():
            for text in text_list:
                f.write(key + ' ' + text + '\n')

filename = 'Flickr8k_text/Flickr8k.token.txt'
doc = load_doc(filename)
texts = load_descriptions(doc)
print('Loaded: %d' % len(texts))
vocabulary = clean_description(texts)
print('Vocabulary size = %d' % len(vocabulary))
save_texts(texts, 'descriptions.txt')


'''
    finance calculator
'''
def main():  # pylint: disable=R0914
    """ This is the main routine for the program """
    print("Starting the sequence:")
    amount_in = 0
    amount_out = 0
    count = 0
    year17_amount = 0
    year18_amount = 0
    year19_amount = 0
    year20_amount = 0
    year21_amount = 0
    year22_amount = 0
    year17_out = 0
    year18_out = 0
    year19_out = 0
    year20_out = 0
    year21_out = 0
    year22_out = 0

    with open("Y:/house-data/consolidated.csv", encoding='utf-8-sig') as finfile:
        for line in finfile:
            if line.startswith("Transaction Date"):
                continue
            data = line.split(',')
            print(f' date: {data[0]} desc: {data[4]} amount-out: {data[5]} amount-in: {data[6]}')
            count += 1
            datedata = data[0].split('/')

            if data[5].strip() == "":
                amount_in += float(data[6])
                if datedata[2] == '2017':
                    year17_amount += float(data[6])
                if datedata[2] == '2018':
                    year18_amount += float(data[6])
                if datedata[2] == '2019':
                    year19_amount += float(data[6])
                if datedata[2] == '2020':
                    year20_amount += float(data[6])
                if datedata[2] == '2021':
                    year21_amount += float(data[6])
                if datedata[2] == '2022':
                    year22_amount += float(data[6])
            else:
                amount_out += float(data[5])
                if datedata[2] == '2017':
                    year17_out += float(data[5])
                if datedata[2] == '2018':
                    year18_out += float(data[5])
                if datedata[2] == '2019':
                    year19_out += float(data[5])
                if datedata[2] == '2020':
                    year20_out += float(data[5])
                if datedata[2] == '2021':
                    year21_out += float(data[5])
                if datedata[2] == '2022':
                    year22_out += float(data[5])
    print('------------------IN----------------------')
    print(f' 2017 total : {year17_amount}')
    print(f' 2018 total : {year18_amount}')
    print(f' 2019 total : {year19_amount}')
    print(f' 2020 total : {year20_amount}')
    print(f' 2021 total : {year21_amount}')
    print(f' 2022 total : {year22_amount}')
    print('------------------IN----------------------')
    print(f' 2017 total : {year17_out}')
    print(f' 2018 total : {year18_out}')
    print(f' 2019 total : {year19_out}')
    print(f' 2020 total : {year20_out}')
    print(f' 2021 total : {year21_out}')
    print(f' 2022 total : {year22_out}')
    print('----------------------------------------')
    print(f'total amount in  : {amount_in}')
    print(f'total amount out : {amount_out}')
    print(f'total amount diff: {amount_in - amount_out}')
    print(f"total records   : {count}")
    print('----------------------------------------')
    print("finishing up and closing down:")

if __name__ == '__main__':
    main()


import random
import time
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
import csv

class BinHeap:
    def __init__(self):
        self.heap_list = [0]
        self.current_size = 0

    def percUp(self, index):
        while index // 2 > 0:
            if self.heap_list[index] < self.heap_list[index // 2]:
                tmp = self.heap_list[index // 2]
                self.heap_list[index // 2] = self.heap_list[index]
                self.heap_list[index] = tmp
            index = index // 2

    def insert(self, k):
        self.heap_list.append(k)
        self.current_size = self.current_size + 1
        self.percUp(self.current_size)

    def findMin(self):
        return self.heap_list[1]

    def percDown(self, index):
        while (index * 2) <= self.current_size:
            mc = self.minChild(index)
            if self.heap_list[index] > self.heap_list[mc]:
                tmp = self.heap_list[index]
                self.heap_list[index] = self.heap_list[mc]
                self.heap_list[mc] = tmp
            index = mc

    def minChild(self, index):
        if index * 2 + 1 > self.current_size:
            return index * 2
        else:
            if self.heap_list[index * 2] < self.heap_list[index * 2 + 1]:
                return index * 2
            else:
                return index * 2 + 1

    def delMin(self):
        retval = self.heap_list[1]
        self.heap_list[1] = self.heap_list[self.current_size]
        self.current_size = self.current_size - 1
        self.heap_list.pop()
        self.percDown(1)
        return retval

    def buildHeap(self, alist):
        index = len(alist) // 2
        self.current_size = len(alist)
        self.heap_list = [0] + alist[:]
        while (index > 0):
            self.percDown(index)
            index = index - 1

    def size(self):
        return self.current_size

    def isEmpty(self):
        return self.current_size == 0

    def __str__(self):
        txt = "{}".format(self.heap_list[1:])
        return txt

def sortHeap(data_list):
    sorted_heap = []
    heap = BinHeap()
    heap.buildHeap(data_list)
    n = len(data_list)
    for i in range(n):
        temp = heap.delMin()
        sorted_heap.append(temp)
    return sorted_heap

def fit_fun(n, a):
    return a * n * np.log(n)

def time_checker(n):
    a = [random.randint(-100, 100) for _ in range(n)]
    start = time.time()
    sortHeap(a)
    stop = time.time()
    return stop - start

def plot(x, y):
    plt.plot(x, y, 'ro', label="Dane")
    plt.xlabel("Liczba elementów")
    plt.ylabel("Czas wykonania [s]")
    plt.legend(loc='upper left')
    plt.title("Wykres czasu w zależności od liczby elementów")
    plt.show()

def hypothesis_plot(x, y, func, popt):
    x2 = np.arange(1, x[-1])

    plt.plot(x, y, 'ro', label="Dane")
    plt.plot(x2, func(x2, *popt), label="Hipoteza", linewidth=3.5)
    plt.xlabel("Liczba elementów")
    plt.ylabel("Czas wykonania [s]")
    plt.legend(loc='upper left')
    plt.title("Wykres czasu w zależności od liczby elementów")
    plt.show()

if __name__ == "__main__":
    n = [1000 * i for i in range(2, 100)]

    execution_times = []

    # for i in n:
    #     execution_times.append(time_checker(i))
    # with open('execution_times.csv', 'w', newline='') as myfile:
    #     wr = csv.writer(myfile, quoting=csv.QUOTE_ALL)
    #     wr.writerow(execution_times)

    with open('execution_times.csv', newline='') as f:
        reader = csv.reader(f)
        execution_times = [float(i) for i in list(reader)[0]]

    plot(n, execution_times)
    popt, pcov = curve_fit(fit_fun, n, execution_times)
    hypothesis_plot(n, execution_times, fit_fun, popt)


import numpy as np
from sklearn import datasets, svm, metrics
from skimage.feature import hog
from sklearn.model_selection import train_test_split
import idx2numpy
from tabulate import tabulate

# Load the MNIST dataset
fileTraining = './data/train-images.idx3-ubyte'
legendTraining = './data/train-labels.idx1-ubyte'
features_train = idx2numpy.convert_from_file(fileTraining)
labels_train = idx2numpy.convert_from_file(legendTraining)
fileTesting = './data/t10k-images.idx3-ubyte'
legendTesting = './data/t10k-labels.idx1-ubyte'
features_test = idx2numpy.convert_from_file(fileTesting)
labels_test = idx2numpy.convert_from_file(legendTesting)

# Reshape the features
features_train = features_train.reshape((features_train.shape[0], -1))
features_test = features_test.reshape((features_test.shape[0], -1))

# Extract HOG features from the training set
hog_features_train = []
for image in features_train:
    hog_features_train.append(hog(image.reshape((28, 28)), orientations=9, pixels_per_cell=(14, 14), cells_per_block=(1, 1), visualize=False))

# Train the SVM classifier
svm_classifier = svm.SVC(kernel='linear')
svm_classifier.fit(hog_features_train, labels_train)

# Extract HOG features from the testing set
hog_features_test = []
for image in features_test:
    hog_features_test.append(hog(image.reshape((28, 28)), orientations=9, pixels_per_cell=(14, 14), cells_per_block=(1, 1), visualize=False))

# Predict labels for the testing set
predicted_labels = svm_classifier.predict(hog_features_test)

# Print classification report
print("Classification Report:")
print(metrics.classification_report(labels_test, predicted_labels))

# Create confusion matrix
confusion_matrix = metrics.confusion_matrix(labels_test, predicted_labels)
print("Confusion Matrix:")
print(tabulate(confusion_matrix, headers=[0,1,2,3,4,5,6,7,8,9], tablefmt='orgtbl'))


# -*- coding: utf-8 -*-
import os

import numpy as np
import torch as torch

from deepctr_torch.callbacks import EarlyStopping, ModelCheckpoint
from deepctr_torch.inputs import SparseFeat, DenseFeat, VarLenSparseFeat

SAMPLE_SIZE = 64

def gen_sequence(dim, max_len, sample_size):
    return np.array([np.random.randint(0, dim, max_len) for _ in range(sample_size)]), np.random.randint(1, max_len + 1,
                                                                                sample_size)

def get_test_data(sample_size=1000, embedding_size=4, sparse_feature_num=1, dense_feature_num=1,
                  sequence_feature=['sum', 'mean', 'max'], classification=True, include_length=False,
                  hash_flag=False, prefix=''):
    feature_columns = []
    model_input = {}

    if 'weight' in sequence_feature:
        feature_columns.append(
            VarLenSparseFeat(SparseFeat(prefix + "weighted_seq", vocabulary_size=2, embedding_dim=embedding_size),
                             maxlen=3, length_name=prefix + "weighted_seq" + "_seq_length",
                             weight_name=prefix + "weight"))
        s_input, s_len_input = gen_sequence(
            2, 3, sample_size)

        model_input[prefix + "weighted_seq"] = s_input
        model_input[prefix + 'weight'] = np.random.randn(sample_size, 3, 1)
        model_input[prefix + "weighted_seq" + "_seq_length"] = s_len_input
        sequence_feature.pop(sequence_feature.index('weight'))

    for i in range(sparse_feature_num):
        dim = np.random.randint(1, 10)
        feature_columns.append(SparseFeat(prefix + 'sparse_feature_' + str(i), dim, embedding_size, dtype=torch.int32))
    for i in range(dense_feature_num):
        feature_columns.append(DenseFeat(prefix + 'dense_feature_' + str(i), 1, dtype=torch.float32))
    for i, mode in enumerate(sequence_feature):
        dim = np.random.randint(1, 10)
        maxlen = np.random.randint(1, 10)
        feature_columns.append(
            VarLenSparseFeat(SparseFeat(prefix + 'sequence_' + mode, vocabulary_size=dim, embedding_dim=embedding_size),
                             maxlen=maxlen, combiner=mode))

    for fc in feature_columns:
        if isinstance(fc, SparseFeat):
            model_input[fc.name] = np.random.randint(0, fc.vocabulary_size, sample_size)
        elif isinstance(fc, DenseFeat):
            model_input[fc.name] = np.random.random(sample_size)
        else:
            s_input, s_len_input = gen_sequence(
                fc.vocabulary_size, fc.maxlen, sample_size)
            model_input[fc.name] = s_input
            if include_length:
                fc.length_name = prefix + "sequence_" + str(i) + '_seq_length'
                model_input[prefix + "sequence_" + str(i) + '_seq_length'] = s_len_input

    if classification:
        y = np.random.randint(0, 2, sample_size)
    else:
        y = np.random.random(sample_size)

    return model_input, y, feature_columns

def layer_test(layer_cls, kwargs={}, input_shape=None,
               input_dtype=torch.float32, input_data=None, expected_output=None,
               expected_output_shape=None, expected_output_dtype=None, fixed_batch_size=False):
    '''check layer is valid or not

    :param layer_cls:
    :param input_shape:
    :param input_dtype:
    :param input_data:
    :param expected_output:
    :param expected_output_dtype:
    :param fixed_batch_size:

    :return: output of the layer
    '''
    if input_data is None:
        # generate input data
        if not input_shape:
            raise ValueError("input shape should not be none")

        input_data_shape = list(input_shape)
        for i, e in enumerate(input_data_shape):
            if e is None:
                input_data_shape[i] = np.random.randint(1, 4)

        if all(isinstance(e, tuple) for e in input_data_shape):
            input_data = []
            for e in input_data_shape:
                rand_input = (10 * np.random.random(e))
                input_data.append(rand_input)
        else:
            rand_input = 10 * np.random.random(input_data_shape)
            input_data = rand_input

    else:
        # use input_data to update other parameters
        if input_shape is None:
            input_shape = input_data.shape

    if expected_output_dtype is None:
        expected_output_dtype = input_dtype

    # layer initialization
    layer = layer_cls(**kwargs)

    if fixed_batch_size:
        inputs = torch.tensor(input_data.unsqueeze(0), dtype=input_dtype)
    else:
        inputs = torch.tensor(input_data, dtype=input_dtype)

    # calculate layer's output
    output = layer(inputs)

    if not output.dtype == expected_output_dtype:
        raise AssertionError("layer output dtype does not match with the expected one")

    if not expected_output_shape:
        raise ValueError("expected output shape should not be none")

    actual_output_shape = output.shape
    for expected_dim, actual_dim in zip(expected_output_shape, actual_output_shape):
        if expected_dim is not None:
            if not expected_dim == actual_dim:
                raise AssertionError(f"expected_dim:{expected_dim}, actual_dim:{actual_dim}")

    if expected_output is not None:
        # check whether output equals to expected output
        assert_allclose(output, expected_output, rtol=1e-3)

    return output

def check_model(model, model_name, x, y, check_model_io=True):
    '''
    compile model,train and evaluate it,then save/load weight and model file.
    :param model:
    :param model_name:
    :param x:
    :param y:
    :param check_model_io:
    :return:
    '''
    early_stopping = EarlyStopping(monitor='val_acc', min_delta=0, verbose=1, patience=0, mode='max')
    model_checkpoint = ModelCheckpoint(filepath='model.ckpt', monitor='val_acc', verbose=1,
                                       save_best_only=True,
                                       save_weights_only=False, mode='max', period=1)

    model.compile('adam', 'binary_crossentropy',
                  metrics=['binary_crossentropy', 'acc'])
    model.fit(x, y, batch_size=100, epochs=1, validation_split=0.5, callbacks=[early_stopping, model_checkpoint])

    print(model_name + 'test, train valid pass!')
    torch.save(model.state_dict(), model_name + '_weights.h5')
    model.load_state_dict(torch.load(model_name + '_weights.h5'))
    os.remove(model_name + '_weights.h5')
    print(model_name + 'test save load weight pass!')
    if check_model_io:
        torch.save(model, model_name + '.h5')
        model = torch.load(model_name + '.h5')
        os.remove(model_name + '.h5')
        print(model_name + 'test save load model pass!')
    print(model_name + 'test pass!')

def get_device(use_cuda=True):
    device = 'cpu'
    if use_cuda and torch.cuda.is_available():
        print('cuda ready...')
        device = 'cuda:0'
    return device


"""
Anna Ablove <aablove@umich.edu>
Dan Choe <danchoe@umich.edu>
Adapted from code from EECS 487 at University of Michigan
"""
import torch
import torch.nn as nn
from src.sent_att_model import SentAttNet
from src.word_att_model import WordAttNet
from src.hierarchical_att_model import HierAttNet
import torch.nn.functional as F
from src.utils import matrix_mul, element_wise_mul

class FFNN(nn.Module):
    """Basic feedforward neural network"""

    def __init__(self, hidden_layer_size, batch_size, max_word_length, max_sent_length):
        super().__init__()
        self.word_hidden_size = 50
        self.sent_hidden_size = hidden_layer_size
        cat_size = (self.word_hidden_size) + (self.sent_hidden_size)
        self.batch_size = batch_size
        self.num_classes = 2
        word2vec_path = 'glove.6B.50d.txt'
        self.max_sent_length = max_sent_length
        self.max_word_length = max_word_length
        fc_size = max_word_length * 2
        self.softmax = nn.Softmax()
        self.context = HierAttNet(self.word_hidden_size, self.sent_hidden_size, self.batch_size, self.num_classes,
                                  word2vec_path, self.max_sent_length, self.max_word_length)
        self.reply = HierAttNet(self.word_hidden_size, self.sent_hidden_size, self.batch_size, self.num_classes,
                                word2vec_path, self.max_sent_length, self.max_word_length)
        self.fc = nn.Linear(2 * self.max_sent_length, self.num_classes)
        self._init_hidden_state()

    def _init_hidden_state(self, last_batch_size=None):
        if last_batch_size:
            batch_size = last_batch_size
        else:
            batch_size = self.batch_size
        self.reply._init_hidden_state(batch_size)
        self.context._init_hidden_state(batch_size)

    def forward(self, reply_in, context_in):
        rep_out = self.reply(reply_in).requires_grad_(True)
        cont_out = self.context(context_in).requires_grad_(True)
        x = torch.cat((cont_out, rep_out), dim=1).requires_grad_(True)
        # print("x", x.shape)
        x = self.fc(x)
        output = F.softmax(x)  # , dim=1
        # print("Xoutput is", output)
        return output


# -*- coding: utf-8 -*-
import numpy as np

if __name__=='__main__':
    a=np.arange(4)
    b=a.copy()
    c=a
    d=b
    a[0]=12
    print(a)
    print(b)
    print(c)
    print(d)


# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

# 折线图
# 设置图片大小和像素
myfig = plt.figure(2, figsize=(14, 10), dpi=70)

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 用来正常显示负号
plt.rcParams['axes.unicode_minus'] = False

# 使用图像坐标添加子图对象位置
ax1 = myfig.add_axes([0.05, 0.2, 0.5, 0.5])
# [x坐标起点，y坐标起点，x图宽，y图宽]
ax2 = myfig.add_axes([0.5, 0.4, 0.5, 0.5])

x = np.linspace(0, 2*np.pi, 100, endpoint=True)
y = np.sin(x)

ax1.plot(x, np.sin(x), label='$sin(x)$')
ax1.set_title('正弦曲线', color='red', fontsize=18)
ax1.legend(loc='best', fontsize=16)

ax2.plot(x, np.cos(x), label='$cos(x)$')
ax2.set_title('余弦曲线', color='red', fontsize=18)
ax2.legend(loc='best', fontsize=16)

myfig.set_facecolor([0, 1, 0, 0.5])

myfig.savefig('matplotlib_02.png')
myfig.show()


import os
import numpy as np
import sys
from PIL import Image, ImageDraw, ImageFont, ImageOps
import h5py

def create_template_image(oibx,text):

    img = Image.new('L', (200,40), 0)
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype("Arial.ttf", 16)
    draw.text((0, 0), text,255,font=font)
    ibx=img.getbbox()
    ibx_in=(ibx[0],ibx[1],ibx[2]+1,ibx[3]+1)
    img=img.crop(ibx_in)
    img=img.resize(oibx[2:4],resample=Image.BICUBIC)
    ibx=img.getbbox()
    img = ImageOps.invert(img)
    new_im=Image.new('L', standard_size,255)
    new_im.paste(img,(0,0))
    nnew_im=np.array(new_im.getdata(),np.uint8).reshape(new_im.size[1], new_im.size[0])
    return nnew_im

def create_image(old_img):

    old_im=Image.new('L',standard_size,255)
    oim=old_img.convert('L')
    oim=ImageOps.scale(oim,.5,resample=Image.BICUBIC)
    oim=ImageOps.invert(oim)
    oibx=oim.getbbox()
    oim=oim.crop(oibx)
    oim=ImageOps.invert(oim)
    old_im.paste(oim,(0,0))
    nold_im=np.array(old_im.getdata(),np.uint8).reshape(old_im.size[1], old_im.size[0])
    IM=nold_im
    return IM, oibx[2:4]

if ('darwin' in sys.platform):
    path=os.path.expanduser("~/Desktop/luisa-blocks-real")
else:
    path="/ga/amit/Desktop/luisa-blocks-real"

print("path")

# Check that text is only regular letters
def all_alfa(text):

    alfa='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
    lent=len(text)
    c=0
    for t in text:
        if (t in alfa):
            c+=1
    alf=c==lent
    return alf

# Read in image and test and save in global lists
def produce_image(r):
    with open (r, "r") as f:
        data=f.readlines()
        if (len(data)>0):
            if (all_alfa(data[0])):
                oldr = r[0:r.find('txt')] + 'tif'
                oldimg=Image.open(oldr)
                ra = r.split('/')[-1]
                if (len(data[0])<max_length):
                    text=data[0]+' '*(max_length-len(data[0]))
                else:
                    text=data[0][0:max_length]
                IM, ibx=create_image(oldimg)
                global Images, IBX, TEXT
                Images+=[IM]
                IBX+=[ibx]
                TEXT+=[text]

# Save as hdf5 file
def make_hpy():
    with h5py.File('pairs.hdf5', 'w') as f:
        dset1 = f.create_dataset("PAIRS", data=np.array(Images))
    with open('texts.txt','w') as f:
        for tx in TEXT:
            f.write('%s\n' % tx)

def im_proc(path,rr):
    for r in rr:
        if 'txt' in r:
            produce_image(path+'/'+r)
            if (len(TEXT)>=num_images):
                print("Hello", len(Images))
                make_hpy()
                exit()

# Recursively check if folder has images or has only subfolders.
def check_if_has_images(path):
    rr=os.listdir(path)
    rr.sort()
    print(len(rr), len(TEXT))
    if 'tif' in rr[0] or 'txt' in rr[0]:
        im_proc(path,rr)
    else:
        for r in rr:
            if not r.startswith('.'):
                check_if_has_images(path+'/'+r)

Images=[]
IBX=[]
TEXT=[]

# Number of images to read, max-length of string to use.
num_images=np.int32(sys.argv[1])
max_length=np.int32(sys.argv[2])
# Standard size of image for text of that length 120x35
sandard_size=(120,35)
if (len(sys.argv)>3):
    standard_size=(np.int32(sys.argv[3]),np.int32(sys.argv[4]))
# Loop over subfolders and process images.
check_if_has_images(path)
make_hpy()


import pathlib
import ezdxf
from ezdxf import zoom

CWD = pathlib.Path("~/Desktop/Outbox").expanduser()
if not CWD.exists():
    CWD = pathlib.Path(".")

# ------------------------------------------------------------------------------
# This example shows how to convert an ARC into a SPLINE.
# ------------------------------------------------------------------------------

def main():
    doc = ezdxf.new()
    msp = doc.modelspace()

    arc = msp.add_arc(
        center=(0, 0),
        radius=1.0,
        start_angle=0,
        end_angle=360,
        dxfattribs={"layer": "arc"},
    )

    spline = arc.to_spline(replace=False)
    spline.dxf.layer = "B-spline"
    spline.dxf.color = 1

    zoom.extents(msp)
    doc.saveas(CWD / "spline_from_arc.dxf")

if __name__ == "__main__":
    main()


import itertools
import logging
from timeit import default_timer
from typing import List, Optional, Tuple

import numpy as np

from python_tsp.heuristics.perturbation_schemes import neighborhood_gen
from python_tsp.heuristics.local_search import setup
from python_tsp.utils import compute_permutation_distance

# logger = logging.getLogger(__name__)
# ch = logging.StreamHandler()
# ch.setLevel(level=logging.WARNING)
# logger.addHandler(ch)

def solve_tsp_simulated_annealing(
    distance_matrix: np.ndarray,
    x0: Optional[List[int]] = None,
    perturbation_scheme: str = "two_opt",
    alpha: float = 0.9,
    max_processing_time: float = None,
    log_file: Optional[str] = None,
) -> Tuple[List, float]:
    """Solve a TSP problem using a Simulated Annealing
    The approach used here is the one proposed in [1].
    Parameters
    ----------
    distance_matrix
        Distance matrix of shape (n x n) with the (i, j) entry indicating the
        distance from node i to j
    x0
        Initial permutation. If not provided, it starts with a random path
    perturbation_scheme {"ps1", "ps2", "ps3", "ps4", "ps5", "ps6", ["two_opt"]}
        Mechanism used to generate new solutions. Defaults to "two_opt"
    alpha
        Reduction factor (``alpha`` < 1) used to reduce the temperature. As a
        rule of thumb, 0.99 takes longer but may return better solutions, whike
        0.9 is faster but may not be as good. A good approach is to use 0.9
        (as default) and if required run the returned solution with a local
        search.
    max_processing_time {None}
        Maximum processing time in seconds. If not provided, the method stops
        only when there were 3 temperature cycles with no improvement.
    log_file
        If not `None`, creates a log file with details about the whole
        execution
    Returns
    -------
    A permutation of nodes from 0 to n - 1 that produces the least total
    distance obtained (not necessarily optimal).
    The total distance the returned permutation produces.
    References
    ----------
    [1] Dréo, Johann, et al. Metaheuristics for hard optimization: methods and
    case studies. Springer Science & Business Media, 2006.
    """

    x, fx = setup(distance_matrix, x0)
    temp = _initial_temperature(distance_matrix, x, fx, perturbation_scheme)
    max_processing_time = max_processing_time or np.inf
#     if log_file:
#         fh = logging.FileHandler(log_file)
#         fh.setLevel(logging.INFO)
#         logger.addHandler(fh)
#         logger.setLevel(logging.INFO)

    logging.info("Tsp start solving")
    n = len(x)
    k_inner_min = n  # min inner iterations
    k_inner_max = 10 * n  # max inner iterations
    k_noimprovements = 0  # number of inner loops without improvement

    tic = default_timer()
    stop_early = False
    while (k_noimprovements < 3) and (not stop_early):
        k_accepted = 0  # number of accepted perturbations
        for k in range(k_inner_max):
            if default_timer() - tic > max_processing_time:
                logging.info("Stopping early due to time constraints")
                stop_early = True
                break

            xn = _perturbation(x, perturbation_scheme)
            fn = compute_permutation_distance(distance_matrix, xn)

            if _acceptance_rule(fx, fn, temp):
                x, fx = xn, fn
                k_accepted += 1
                k_noimprovements = 0

#             logger.info(
#                 f"Temperature {temp}. Current value: {fx} "
#                 f"k: {k + 1}/{k_inner_max} "
#                 f"k_accepted: {k_accepted}/{k_inner_min} "
#                 f"k_noimprovements: {k_noimprovements}"
#             )

            if k_accepted >= k_inner_min:
                break

        temp *= alpha  # temperature update
        k_noimprovements += k_accepted == 0

    logging.info("Tsp stop solving")

    return x, fx

def _initial_temperature(
    distance_matrix: np.ndarray,
    x: List[int],
    fx: float,
    perturbation_scheme: str,
) -> float:
    """Compute initial temperature
    Instead of relying on problem-dependent parameters, this function estimates
    the temperature using the suggestion in [1].
    Notes
    -----
    Here are the steps followed:
        1. Generate 100 disturbances at random from T0, and evaluate the mean
        objective value differences dfx_mean = mean(fn - fx);
        2. Choose tau0 = 0.5 as assumed quality of initial solution (assuming
        a bad one), and deduce T0 from exp(-fx_mean/T0) = tau0, that is,
        T0 = -fx_mean/ln(tau0)
    References
    ----------
    [1] Dréo, Johann, et al. Metaheuristics for hard optimization: methods and
    case studies. Springer Science & Business Media, 2006.
    """

    # Step 1
    dfx_list = []
    for _ in range(100):
        xn = _perturbation(x, perturbation_scheme)
        fn = compute_permutation_distance(distance_matrix, xn)
        dfx_list.append(fn - fx)

    dfx_mean = np.abs(np.mean(dfx_list))

    # Step 2
    tau0 = 0.5
    return -dfx_mean / np.log(tau0)

def _perturbation(x: List[int], perturbation_scheme: str):
    """Generate a random neighbor of a current solution ``x``
    In this case, we can use the generators created in the `local_search`
    module, and pick the first solution. Since the neighborhood is randomized,
    it is the same as creating a random perturbation.
    """
    return next(neighborhood_gen[perturbation_scheme](x))

def _acceptance_rule(fx: float, fn: float, temp: float) -> bool:
    """Metropolis acceptance rule"""

    dfx = fn - fx
    return (dfx < 0) or (
        (dfx > 0) and (np.random.rand() <= np.exp(-(fn - fx) / temp))
    )

def tsp_brute_force(pairwise_dist: np.ndarray):
    num_batches = pairwise_dist.shape[0]
    permute = itertools.permutations(list(range(num_batches)))

    best_dist = -1.
    best_perm = None

    for term in permute:
        term = list(term)
        term.append(term[0])
        dist = pairwise_dist[term[:-1], term[1:]].sum()
        if dist > best_dist:
            best_dist = dist
            best_perm = term[:-1]

    return best_perm, best_dist

def tsp_heuristic(pairwise_dist: np.ndarray):
    assert np.all(pairwise_dist >= 0.)

    best_perm, best_dist = solve_tsp_simulated_annealing(-pairwise_dist,
                                                         max_processing_time=1800)

    return best_perm, best_dist


#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

AUTHOR = 'winlu'
SITENAME = 'pelican single page showoff'
SITEURL = 'http://single.heroicdebugging.biz'

PATH = 'content'

TIMEZONE = 'Europe/Paris'

DEFAULT_LANG = 'en'

# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None

###theme
THEME = "/home/winlu/gitrepos/pelican_my_themes/pelican-single-page"

DIRECT_TEMPLATES = ('index',)
PAGE_SAVE_AS = ''