     newObj[k] = v
   return newObj
 
+makeArray = (obj) ->
+  if obj? then (if obj.splice then obj else [obj]) else []
+
 acc = (column) ->
   throw "no such column" unless column?
   return column if typeof column is 'function'
   return (d) -> d[column]
 
-cross_data_ctx = (data, scale, extra) ->
-  newData = []
-  for datum in data
-    newData.push {
-      d: datum
-      s: scale
-      e: extra
-    }
-  return newData
+cross = (arrays) ->
+  vectorName = []
+  vectorValues = []
+  retLength = 1
+  for k,vs of arrays
+    return [] unless vs.splice
+    vectorName.push(k)
+    vectorValues.push(vs)
+    retLength *= vs.length
+
+  return [] unless vectorValues.length
+
+  ret = []
+  i = 0
+  while i < retLength
+    row = {}
+    k = i
+    for vs,j in vectorValues
+      t = k % vs.length
+      row[vectorName[j]] = vs[t]
+      k = Math.floor(k / vs.length)
+
+    ret[i] = row
+    i++
+
+  return ret
+
+
+window.tile = {
+  vertical: (rect, datas) ->
+    total = 0
+    for d in datas
+      total += d
+
+    scale = rect.height / total
+    rects = []
+    y = 0
+    for d in datas
+      h = d * scale
+      rects.push {
+        x: 0
+        y: y
+        width: rect.width
+        height: h
+      }
+      y += h
+
+    return rects
+}
+
+# ------------------------------------------------------------------
 
 # Valid:
 # {name, column} = {name}
 # {name, type: all }
-
-binMap = {
-  millisecond: 1
-  second: 1000
-  minute: 60 * 1000
-  hour: 60 * 60 * 1000
-  day: 24 * 60 * 60 * 1000
-  week: 7 * 24 * 60 * 60 * 1000
-}
-make_dimension = (name, {column, type, bin}) ->
-  column = name.toLowerCase() unless column
-  switch type
-    when 'all'
-      fn = (row) -> '$all'
-    when 'categorical'
-      throw 'categorical dimension must have column' unless column
-      fn = (row) -> row[column]
-    when 'ordinal'
-      throw 'ordinal dimension must have column' unless column
-      fn = (row) -> row[column]
-    when 'continuous'
-      throw 'continuous dimension must have column' unless column
-      throw 'continuous dimension must have bin' unless bin
-      bin = binMap[bin] if binMap[bin]
-      fn = (row) -> Math.floor(row[column] / bin)
-    else
-      throw 'must have a type'
-  fn.role = 'dimension'
-  fn.$name = name
-  return fn
-
-is_dimension = (d) -> typeof d is 'function' and d.role is 'dimension' and d.$name
+facet.dimension = do ->
+  binMap = {
+    millisecond: 1
+    second: 1000
+    minute: 60 * 1000
+    hour: 60 * 60 * 1000
+    day: 24 * 60 * 60 * 1000
+    week: 7 * 24 * 60 * 60 * 1000
+  }
+  return (name, {column, type, bin}) ->
+    throw "name can not start with $  (#{name})" if name[0] is '$'
+    column = name.toLowerCase() unless column
+    switch type
+      when 'all'
+        fn = (row) -> '$all'
+      when 'categorical'
+        throw 'categorical dimension must have column' unless column
+        fn = (row) -> row[column]
+      when 'ordinal'
+        throw 'ordinal dimension must have column' unless column
+        fn = (row) -> row[column]
+      when 'continuous'
+        throw 'continuous dimension must have column' unless column
+        throw 'continuous dimension must have bin' unless bin
+        bin = binMap[bin] if binMap[bin]
+        fn = (row) -> Math.floor(row[column] / bin)
+      else
+        throw 'must have a type'
+    fn.$name = name
+    return fn
 
 # Valid:
 # {column}
 # {agg: 'count' }
-make_metric = (name {column, agg}) ->
-  column = name.toLowerCase() unless column
-  if agg is 'const'
-    fn = (rows) -> 1
-  else if agg is 'count'
-    fn = (rows) -> rows.length
-  else if column
-    a = aggregartion[agg]
-    throw "invalid agg (#{agg})" unless a
-    fn = (rows) -> a(rows.map((d) -> d[column]))
-  else
-    throw "needs agg == 'count' or column"
-  fn.role = 'metric'
-  fn.$name = name
-  return fn
-
-is_metric = (m) -> typeof m is 'function' and m.role is 'metric' and m.$name
-
+facet.metric = do ->
+  aggregartion = {
+    sum: (values) ->
+      sum = 0
+      for v in values
+        sum += v
+      return sum
+
+    average: (values) ->
+      sum = 0
+      for v in values
+        sum += v
+      return sum / values.length
+
+    uniq: (values) ->
+      seen = {}
+      count = 0
+      for v in values
+        continue if seen[v]
+        count++
+        seen[v] = 1
+      return count
+
+    common: (values) ->
+      counts = {}
+      # to do
+      return values[0]
+  }
+  return (name, {column, agg}) ->
+    throw "name can not start with $  (#{name})" if name[0] is '$'
+    column = name.toLowerCase() unless column
+    if agg is 'const'
+      fn = (rows) -> 1
+    else if agg is 'count'
+      fn = (rows) -> rows.length
+    else if column
+      a = aggregartion[agg]
+      throw "invalid agg (#{agg})" unless a
+      fn = (rows) -> a(rows.map((d) -> d[column]))
+    else
+      throw "needs agg == 'count' or column"
+    fn.$name = name
+    return fn
+
+
+# combine =
+#  sort: <dimension or metric name>
+#  order: asc | desc
+#  skip: <number>
+#  limit: <number>
 facet.data = (rows) ->
   splits = []
+  dimensions = {}
+  metrics = {}
 
   makeSac = ({split, apply, combine}, breakdown) -> (rows) ->
     buckets = {}


      for metric in apply
        v = metric(bucket.rows)
        newRow[metric.column] = v

      out.push(newRow)

    return out

  me = {
    sac: (split, apply, combine) ->
      throw "split must be a dimension" unless is_dimension(split) or split.splice?
      throw "apply must be a metric or a list of metrics" unless is_metric(apply) or apply.splice?
      split = [split] if not split.splice
      apply = [apply] if not apply.splice
      splits.push { split, apply, combine }
      return me

    # outputs a list  []
    get: ->
      numSplits = splits.length
      throw 'no splits defined' unless splits

      dummy = {}
      dummy['$split'] = rows

      stage = [dummy]
      for s,i in splits
        sacFn = makeSac(s, i < numSplits-1)
        newStages = []
        for st in stage
          mappedRows = sacFn(st['$split'])
          st['$split'] = mappedRows
          newStages.push(mappedRows)

        stage = Array::concat.apply([], newStages)

      return dummy['$split']
  }
  return me


# ------------------------------------------

scales = {
  linear: (data, fn, flipRange = false) ->
    s = d3.scale.linear()
      .domain([d3.min(data, fn), d3.max(data, fn)])
      .range(if flipRange then [1,0] else [0,1])
    return s

  color: (data, fn) ->
    s = d3.scale.category10()
      .domain(data.map(fn))
    return s
}

facet.plot = ({selector, size, dataSource, plot}) ->
  svg = d3.select(selector)
    .append('svg')
    .attr('class', 'facet')
    .attr('width',  size.width)
    .attr('height', size.height)

  data = dataSource.data

  ###
  scale: {

# Dimension = {row} -> String
# Metric = {row} -> Number
# [Metric] = {row} -> [Number]

sortedUniq = (arr) ->
  u = []
