import {ticks} from "d3-array";
import {format} from "d3-format";
import nice from "./nice";
import {default as continuous, copy} from "./continuous";

function powp(base) {
  return base === Math.E ? Math.exp
      : function(x) { return Math.pow(base, x); };
}

function logp(base) {
  return base === Math.E ? Math.log
      : base === 10 && Math.log10
      || base === 2 && Math.log2
      || (base = Math.log(base), function(x) { return Math.log(x) / base; });
}

function reflect(f) {
  return function(x) {
    return -f(-x);
  };
}

export default function log() {
  var scale = continuous(transform).domain([1, 10]),
      domain = scale.domain,
      base = 10,
      logs = logp(10),
      pows = powp(10);

  function transform(x) {
    return logs(x);
  }

  transform.invert = function(x) {
    return pows(x);
  };

  function rescale() {
    logs = logp(base), pows = powp(base);
    if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);
    return scale;
  }

  scale.base = function(_) {
    return arguments.length ? (base = +_, rescale()) : base;
  };

  scale.domain = function(_) {
    return arguments.length ? (domain(_), rescale()) : domain();
  };

  scale.ticks = function(count) {
    var d = domain(),
        u = d[0],
        v = d[d.length - 1],
        r;

    if (r = v < u) i = u, u = v, v = i;

    var i = logs(u),
        j = logs(v),
        p,
        k,
        t,
        n = count == null ? 10 : +count,
        z = [];

    if (!(base % 1) && j - i < n) {
      i = Math.round(i) - 1, j = Math.round(j) + 1;
      if (u > 0) for (; i < j; ++i) {
        for (k = 1, p = pows(i); k < base; ++k) {
          t = p * k;
          if (t < u) continue;
          if (t > v) break;
          z.push(t);
        }
      } else for (; i < j; ++i) {
        for (k = base - 1, p = pows(i); k >= 1; --k) {
          t = p * k;
          if (t < u) continue;
          if (t > v) break;
          z.push(t);
        }
      }
    } else {
      z = ticks(i, j, Math.min(j - i, n)).map(pows);
    }

    return r ? z.reverse() : z;
  };

  scale.tickFormat = function(count, specifier) {
    if (specifier == null) specifier = base === 10 ? ".0e" : ",";
    if (typeof specifier !== "function") specifier = format(specifier);
    if (count === Infinity) return specifier;
    if (count == null) count = 10;
    var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?
    return function(d) {
      var i = d / pows(Math.round(logs(d)));
      if (i * base < base - 0.5) i *= base;
      return i <= k ? specifier(d) : "";
    };
  };

  scale.nice = function() {
    return domain(nice(domain(), {
      floor: function(x) { return pows(Math.floor(logs(x))); },
      ceil: function(x) { return pows(Math.ceil(logs(x))); }
    }));
  };

  scale.copy = function() {
    return copy(scale, log().base(base));
  };

  return scale;
}

@@ -157,34 +157,137 @@
 tion pow
-p(base) %7B%0A  return
+10(x) %7B%0A  return isFinite(x) ? +(%221e%22 + x) : x %3C 0 ? 0 : x;%0A%7D%0A%0Afunction powp(base) %7B%0A  return base === 10 ? pow10%0A      :
  base ==
@@ -769,16 +769,62 @@
 e = 10,%0A
+      logt = Math.log,%0A      powt = Math.exp,%0A
       lo
@@ -902,17 +902,17 @@
 turn log
-s
+t
 (x);%0A  %7D
@@ -962,17 +962,17 @@
 turn pow
-s
+t
 (x);%0A  %7D
@@ -993,24 +993,62 @@
 rescale() %7B%0A
+    logt = Math.log, powt = Math.exp;%0A
     logs = l
@@ -1101,16 +1101,74 @@
 %5B0%5D %3C 0)
+ %7B%0A      logt = reflect(logt), powt = reflect(powt);%0A     
  logs = 
@@ -1200,24 +1200,30 @@
 lect(pows);%0A
+    %7D%0A
     return s

Optimize log transform.