text: import {default as accessor, accessorFields} from './accessor';
  import array from './array';
  import field from './field';
  import isFunction from './isFunction';
  
  const DESCENDING = 'descending';
  
  export default function(fields, orders, opt) {
    opt = opt || {};
    orders = array(orders) || [];
  
    const ord = [], get = [], fmap = {},
          gen = opt.comparator || comparator;
  
    array(fields).forEach((f, i) => {
      if (f == null) return;
      ord.push(orders[i] === DESCENDING ? -1 : 1);
      get.push(f = isFunction(f) ? f : field(f, null, opt));
      (accessorFields(f) || []).forEach(_ => fmap[_] = 1);
    });
  
    return get.length === 0
      ? null
      : accessor(gen(get, ord), Object.keys(fmap));
  }
  
  export const ascending = (u, v) => (u < v || u == null) && v != null ? -1
    : (u > v || v == null) && u != null ? 1
    : ((v = v instanceof Date ? +v : v), (u = u instanceof Date ? +u : u)) !== u && v === v ? -1
    : v !== v && u === u ? 1
    : 0;
  
  const comparator = (fields, orders) => fields.length === 1
    ? compare1(fields[0], orders[0])
    : compareN(fields, orders, fields.length);
  
  const compare1 = (field, order) => function(a, b) {
    return ascending(field(a), field(b)) * order;
  };
  
  const compareN = (fields, orders, n) => {
    orders.push(0); // pad zero for convenient lookup
    return function(a, b) {
      let f, c = 0, i = -1;
      while (c === 0 && ++i < n) {
        f = fields[i];
        c = ascending(f(a), f(b));
      }
      return c * orders[i];
    };
  };
  