from __future__ import division,print_function
import sys,random,re
sys.dont_write_bytecode =True

def settings(**d): return o(
  name="KEYSWEST v0.1",
  what="A linear-time MOEA/D variant",
  _logo="""
            .-""-.
           / .--. \ 
          / /    \ \ 
          | |    | |
          | |.-""-.|
         ///`.::::.`\ 
        ||| ::/  \:: ;
        ||; ::\__/:: ;
         \\\ '::::' /
     jgs  `=':-..-'`
    """,
  synopsis="""
  Read tabular data from disk.  Find 'islands'-
  leaves of a dendogram found via recursive PCA-like
  clustering. Then discretize the numerics via
  minimizing the entropy of the island ids
  associated with the numeric values.  Apply 'envy'
  to find islands 'better' than some 'current'
  island.  Sort ranges via their frequency in better
  and current.  Greedy search the sort to find the
  best fewest ranges.
  """,
  author="Tim Menzies",
  copyleft="(c) 2014, MIT license, http://goo.gl/3UYBp",
  secure = False,
  seed=1,
  tiny=0.5,
  start='print(The._logo)',
  cache = o(size=256),
  read  = o(sep      = ",",
            bad      = r'(["\' \t\r\n]|#.*)',
            skip     = '?',
            numc     = '$',
            less     = '<',
            more     = '>',
            klass    = '=',
            missing  = '?',
            )
  ).update(**d)

class o:
  def __init__(i,**d): i.update(**d)
  def update(i,**d): i.__dict__.update(**d); return i
  def __repr__(i)   : 
    d    = i.__dict__
    show = [':%s %s' % (k,d[k]) 
            for k in sorted(d.keys() ) 
            if k[0] is not "_"]
    return '{'+' '.join(show)+'}'

The= settings()

rand= random.random
seed= random.seed
any = random.choice
def gt(x,y): return x > y
def lt(x,y): return x < y

def say(*lst):
  sys.stdout.write(' '.join(map(str,lst)))
  sys.stdout.flush()
def sayln(*lst):
  say(*lst); print("")

def _say(): sayln(1,2,3,4)

def cmd(com=The.start):
  if not The.secure:
    if globals()["__name__"] == "__main__":
      if len(sys.argv) == 3:
        if sys.argv[1] == '--cmd':
          com = sys.argv[2] + '()'
      if len(sys.argv) == 4:
        com = sys.argv[2] + '(' + sys.argv[3] + ')'
    eval(com)

def medianIQR(lst, ordered=False):
  lst = lst if ordered else sorted(lst)
  n   = len(lst)
  q   = n//4
  iqr = lst[q*3] - lst[q]
  if n % 2:  return lst[q*2],iqr
  p = max(0,q-1)
  return (lst[p] + lst[q]) * 0.5,iqr

def atom(x):
  try : return int(x)
  except ValueError:
    try : return float(x)
    except ValueError : return x

#########################################################

def table(source):
  tbl = o(rows=[],head=None)
  for n,cells in row(source): 
    if n == 0 : 
      f = factory0(cells)
      tbl.head = f()
      meta = tbl.head.cols[0]
    else : 
      cells = [meta] + cells
      tbl.head += cells
      tbl.rows += [cells]
  return tbl

def row(file):
  """Leaps over any columns marked 'skip'.
  Turn strings to numbers or strings. 
  Kill comments. Join lines that end in 'sep'."""
  skip = The.read.skip
  sep  = The.read.sep
  bad  = The.read.bad
  def rows(): 
    n,kept = 0,""
    for line in open(file):
      now   = re.sub(bad,"",line)
      kept += now
      if kept:
        if not now[-1] == sep:
          yield n, map(atom, kept.split(sep))
          n += 1
          kept = "" 
  todo = None
  for n,line in rows():
    todo = todo or [col for col,name in enumerate(line) 
                    if not skip in name]
    yield n, [ line[col] for col in todo ]

def _rows(f='data/nasa93.csv'):
   for n,cells in row(f): 
     print(n,cells)

#########################################################
class Col:
  def any(i): return None
  def dist(i,x,y): return 0
  def norm(i,x) : return x
  def __iadd__(i,x):
    if not x is None:
      i.add(x)
    return i

class Cache(Col):
  "Keep a random sample of stuff seen so far."
  def any(i): return any(i.all)