declare module "d3" {



/**
 * Administrivia: JavaScript primitive types and Date
 */
export type Primitive = number | string | boolean | Date;

/**
 * Administrivia: anything with a valueOf(): number method is comparable, so we allow it in numeric operations
 */
interface Numeric {
    valueOf(): number;
}




/**
 * Return the maximum value in the array of numbers using natural order.
 */
export function max(array: number[]): number | undefined;

/**
 * Return the maximum value in the array of strings using natural order.
 */
export function max(array: string[]): string | undefined;

/**
 * Return the maximum value in the array of numbers using natural order.
 */
export function max<T extends Numeric>(array: T[]): T | undefined;

/**
 * Return the maximum value in the array using natural order and a projection function to map values to numbers.
 */
export function max<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): number | undefined;

/**
 * Return the maximum value in the array using natural order and a projection function to map values to strings.
 */
export function max<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => string): string | undefined;

/**
 * Return the maximum value in the array using natural order and a projection function to map values to easily-sorted values.
 */
export function max<T, U extends Numeric>(array: T[], accessor: (datum: T, index: number, array: T[]) => U): U | undefined;

/**
 * Return the minimum value in the array using natural order.
 */
export function min(array: number[]): number | undefined;

/**
 * Return the minimum value in the array using natural order.
 */
export function min(array: string[]): string | undefined;

/**
 * Return the minimum value in the array using natural order.
 */
export function min<T extends Numeric>(array: T[]): T | undefined;

/**
 * Return the minimum value in the array using natural order.
 */
export function min<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): number | undefined;

/**
 * Return the minimum value in the array using natural order.
 */
export function min<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => string): string | undefined;

/**
 * Return the minimum value in the array using natural order.
 */
export function min<T, U extends Numeric>(array: T[], accessor: (datum: T, index: number, array: T[]) => U): U | undefined;



/**
 * Return the min and max simultaneously.
 */
export function extent(array: number[]): [number, number] | [undefined, undefined];

/**
 * Return the min and max simultaneously.
 */
export function extent(array: string[]): [string, string] | [undefined, undefined];

/**
 * Return the min and max simultaneously.
 */
export function extent<T extends Numeric>(array: T[]): [T, T] | [undefined, undefined];

/**
 * Return the min and max simultaneously.
 */
export function extent<T extends Numeric>(array: Array<T | Primitive>): [T | Primitive, T | Primitive] | [undefined, undefined];

/**
 * Return the min and max simultaneously.
 */
export function extent<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): [number, number] | [undefined, undefined];

/**
 * Return the min and max simultaneously.
 */
export function extent<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => string): [string, string] | [undefined, undefined];

/**
 * Return the min and max simultaneously.
 */
export function extent<T, U extends Numeric>(array: T[], accessor: (datum: T, index: number, array: T[]) => U): [U | Primitive, U | Primitive] | [undefined, undefined];

/**
 * Return the mean of an array of numbers
 */
export function mean(array: number[]): number | undefined;
export function mean<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): number | undefined;

/**
 * Return the median of an array of numbers
 */
export function median(array: number[]): number | undefined;
export function median<T>(array: T[], accessor: (element: T, i: number, array: T[]) => number): number | undefined;

/**
 * Returns the p-quantile of an array of numbers
 */
export function quantile(array: number[], p: number): number | undefined;
export function quantile<T>(array: T[], p: number, accessor: (element: T, i: number, array: T[]) => number): number | undefined;

/**
 * Compute the sum of an array of numbers.
 */
export function sum(array: number[]): number;

/**
 * Compute the sum of an array, using the given accessor to convert values to numbers.
 */
export function sum<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): number;

/**
 * Compute the standard deviation, defined as the square root of the bias-corrected variance, of the given array of numbers.
 */
export function deviation(array: number[]): number | undefined;

/**
 * Compute the standard deviation, defined as the square root of the bias-corrected variance, of the given array,
 * using the given accessor to convert values to numbers.
 */
export function deviation<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): number | undefined;

/**
 * Compute an unbiased estimator of the population variance of the given array of numbers.
 */
export function variance(array: number[]): number | undefined;

/**
 * Compute an unbiased estimator of the population variance of the given array,
 * using the given accessor to convert values to numbers.
 */
export function variance<T>(array: T[], accessor: (datum: T, index: number, array: T[]) => number): number | undefined;



export function scan<T>(array: T[], comparator: (a: T, b: T) => number): number;

export function bisectLeft(array: number[], x: number, lo?: number, hi?: number): number;
export function bisectLeft(array: string[], x: string, lo?: number, hi?: number): number;
export function bisectLeft(array: Date[], x: Date, lo?: number, hi?: number): number;

export function bisectRight(array: number[], x: number, lo?: number, hi?: number): number;
export function bisectRight(array: string[], x: string, lo?: number, hi?: number): number;
export function bisectRight(array: Date[], x: Date, lo?: number, hi?: number): number;

export var bisect: typeof bisectRight;

export interface Bisector<T, U> {
    left: (array: T[], x: U, lo?: number, hi?: number) => number;
    right: (array: T[], x: U, lo?: number, hi?: number) => number;
}

export function bisector<T, U>(accessor: (x: T) => U): Bisector<T, U>;

export function bisector<T, U>(comparator: (a: T, b: U) => number): Bisector<T, U>

/**
 * Compares two primitive values for sorting (in ascending order).
 */
export function ascending(a: Primitive, b: Primitive): number;

/**
 * Compares two primitive values for sorting (in ascending order).
 */
export function descending(a: Primitive, b: Primitive): number;



/**
 * Merges the specified arrays into a single array.
 */
export function merge<T>(arrays: T[][]): T[];

/**
 * For each adjacent pair of elements in the specified array, returns a new array of tuples of elements i and i - 1.
 * Returns the empty array if the input array has fewer than two elements.
 */
export function pairs<T>(array: T[]): Array<[T, T]>;

/**
 * Given the specified array, return an array corresponding to the list of indices in 'keys'.
 */
export function permute<T>(array: { [key: number]: T }, keys: number[]): T[];

/**
 * Given the specified object, return an array corresponding to the list of property names in 'keys'.
 */
export function permute<T>(object: { [key: string]: T }, keys: string[]): T[];


/**
 * Generates a 0-based numeric sequence. The output range does not include 'stop'.
 */
export function range(stop: number): number[];

/**
 * Generates a numeric sequence starting from the given start and stop values. 'step' defaults to 1. The output range does not include 'stop'.
 */
export function range(start: number, stop: number, step?: number): number[];


/**
 * Randomizes the order of the specified array using the Fisher–Yates shuffle.
 */
export function shuffle<T>(array: T[], lo?: number, hi?: number): T[];

/**
 * Generate an array of approximately count + 1 uniformly-spaced, nicely-rounded values between start and stop (inclusive).
 */
export function ticks(start: number, stop: number, count: number): number[];

/**
 * Generate an array of with the differences between adjecent ticks, had the same arguments
 * been passed to ticks(start, stop, count)
 */
export function tickStep(start: number, stop: number, count: number): number[];


/**
 * Transpose a matrix provided in Array of Arrays format.
 */
export function transpose<T>(matrix: T[][]): T[][];


/**
 * Returns an array of arrays, where the ith array contains the ith element from each of the argument arrays.
 * The returned array is truncated in length to the shortest array in arrays. If arrays contains only a single array, the returned array
 * contains one-element arrays. With no arguments, the returned array is empty.
 */
export function zip<T>(...arrays: T[][]): T[][];


export interface Bin<Datum, Value extends number | Date> extends Array<Datum> {
    x0: Value;
    x1: Value;
}

/**
 * Type definition for threshold generator which returns the count of recommended thresholds
 */
export type ThresholdCountGenerator = (values: number[], min?: number, max?: number) => number;

/**
 * Type definition for threshold generator which returns an array of recommended thresholds
 */
export type ThresholdArrayGenerator<Value extends number | Date> = (values: Value[], min?: Value, max?: Value) => Value[];



export interface HistogramGenerator<Datum, Value extends number | Date> {
    (data: Datum[]): Array<Bin<Datum, Value>>;
    value(): (d: Datum, i: number, data: Datum[]) => Value;
    value(valueAccessor: (d: Datum, i: number, data: Datum[]) => Value): this;
    domain(): (values: Value[]) => [Value, Value];
    domain(domain: [Value, Value]): this;
    domain(domainAccessor: (values: Value[]) => [Value, Value]): this;
    thresholds(): ThresholdCountGenerator | ThresholdArrayGenerator<Value>;
    /**
     * Divide the domain uniformly into approximately count bins. IMPORTANT: This threshold
     * setting approach only works, when the materialized values are numbers!
     *
     * @param count The desired number of uniform bins.
     */
    thresholds(count: number): this;
    /**
     * Set a threshold accessor function, which returns the desired number of bins.
     * Divides the domain uniformly into approximately count bins. IMPORTANT: This threshold
     * setting approach only works, when the materialized values are numbers!
     *
     * @param count A function which accepts as arguments the array of materialized values, and
     * optionally the domain minimum and maximum. The function calcutates and returns the suggested
     * number of bins.
     */
    thresholds(count: ThresholdCountGenerator): this;
    /**
     * Set the array of values to be used as thresholds in determining the bins.
     * @param thresholds Array of threshold values used for binning. The elements must
     * be of the same type as the materialized values of the histogram.
     */
    thresholds(thresholds: Value[]): this;
    /**
     * Set a threshold accessor function, which returns the array of values to be used as
     * thresholds in determining the bins.
     *
     * @param thresholds A function which accepts as arguments the array of materialized values, and
     * optionally the domain minimum and maximum. The function calcutates and returns the array of values to be used as
     * thresholds in determining the bins.
     */
    thresholds(thresholds: ThresholdArrayGenerator<Value>): this;
}

export function histogram(): HistogramGenerator<number, number>;
export function histogram<Datum, Value extends number | Date>(): HistogramGenerator<Datum, Value>;


export function thresholdFreedmanDiaconis(values: number[], min: number, max: number): number; // of type ThresholdCountGenerator

export function thresholdScott(values: number[], min: number, max: number): number; // of type ThresholdCountGenerator

export function thresholdSturges(values: number[]): number; // of type ThresholdCountGenerator

import { Selection, TransitionLike } from '../d3-selection';



/**
 * A helper interface to describe the minimal contract to be met by a time interval
 * which can be passed into the Axis.ticks(...) or Axis.tickArguments(...) methods when
 * creating time series axes. Under normal circumstances the argument will be of type
 * TimeInterval or CountableTimeInterval as defined in d3-time.
 * NB: This helper interface has been created to avoid tight coupling of d3-axis to
 * d3-time at the level of definition files. I.e. d3-time is not a
 * dependency of d3-axis in the D3 Javascript implementation. This minimal contract
 * is based on an analysis of how d3-axis passes a time interval argument into a time scale,
 * if a time scale was set using Axis.scale(...). And in turn on how a time scale uses
 * the time interval when creating ticks from it.
 */
export interface AxisTimeInterval {
    range(start: Date, stop: Date, step?: number): Date[];
}

/**
 * A helper interface to which a scale passed into axis must conform (at a minimum)
 * for axis to use the scale without error
 */
export interface AxisScale<Domain> {
    (x: Domain): number;
    domain(): Array<Domain>;
    range(): Array<number>;
    copy(): AxisScale<Domain>;
    bandwidth?(): number;
    ticks?(count: number | AxisTimeInterval): Array<number> | Array<Date>;
    tickFormat?(count: number | AxisTimeInterval, specifier?: string): ((d: number) => string) | ((d: Date) => string);
}

/**
 * A helper type to alias elements which can serve as a container for an axis
 */
export type AxisContainerElement = SVGSVGElement | SVGGElement;

/**
 * Interface defining an axis generator. The generic <Domain> is the type of the axis domain
 */
export interface Axis<Domain> {
    /**
     * Render the axis to the given context.
     *
     * @param context A selection of SVG containers (either SVG or G elements).
     */
    (context: Selection<AxisContainerElement, any, any, any>): void;

    /**
    * Render the axis to the given context.
    *
    * @param context A transition defined on SVG containers (either SVG or G elements).
    */
    (context: TransitionLike<AxisContainerElement, any>): void;

    /**
     * Gets the current scale underlying the axis.
     */
    scale<A extends AxisScale<Domain>>(): A;

    /**
     * Sets the scale and returns the axis.
     *
     * @param scale  The scale to be used for axis generation
     */
    scale(scale: AxisScale<Domain>): this;

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     *
     * @param count Number of ticks that should be rendered
     * @param specifier An optional format specifier to customize how the tick values are formatted.
     */
    ticks(count: number, specifier?: string): this;

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     * Use with a TIME SCALE ONLY.
     *
     * @param interval A time interval used to generate date-based ticks. This is typically a TimeInterval/CountableTimeInterval as defined
     * in d3-time. E.g. as obtained by passing in d3.timeMinute.every(15).
     * @param specifier An optional format specifier to customize how the tick values are formatted.
     */
    ticks(interval: AxisTimeInterval, specifier?: string): this;

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     */
    ticks(arg0: any, ...args: any[]): this;

    /**
     * Get an array containing the currently set arguments to be passed into scale.ticks and scale.tickFormat.
     */
    tickArguments(): any[];

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     *
     * @param args An array containing a single element representing the count, i.e. number of ticks to be rendered.
     */
    tickArguments(args: [number]): this;

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     *
     * @param args An array containing two elements. The first element represents the count, i.e. number of ticks to be rendered. The second
     * element is a string representing the format specifier to customize how the tick values are formatted.
     */
    tickArguments(args: [number, string]): this;

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     * Use with a TIME SCALE ONLY.
     *
     * @param args An array containing a single element representing a time interval used to generate date-based ticks.
     * This is typically a TimeInterval/CountableTimeInterval as defined in d3-time. E.g. as obtained by passing in d3.timeMinute.every(15).
     */
    tickArguments(args: [AxisTimeInterval]): this;

    /**
     * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
     * Use with a TIME SCALE ONLY.
     *
     * @param args An array containing two elements. The first element represents a time interval used to generate date-based ticks.
     * This is typically a TimeInterval/CountableTimeInterval as defined in d3-time. E.g. as obtained by passing in d3.timeMinute.every(15).
     * The second element is a string representing the format specifier to customize how the tick values are formatted.
     */
    tickArguments(args: [AxisTimeInterval, string]): this;

    /**
    * Sets the arguments that will be passed to scale.ticks and scale.tickFormat when the axis is rendered, and returns the axis generator.
    *
    * @param args An array with arguments suitable for the scale to be used for tick generation
    */
    tickArguments(args: any[]): this;

    /**
     * Returns the current tick values, which defaults to null.
     */
    tickValues(): Domain[] | null;

    /**
     * Specified values to be used for ticks rather than using the scale’s automatic tick generator.
     * The explicit tick values take precedent over the tick arguments set by axis.tickArguments.
     * However, any tick arguments will still be passed to the scale’s tickFormat function if a
     * tick format is not also set.
     *
     * @param values An array with values from the Domain of the scale underlying the axis.
     */
    tickValues(values: Domain[]): this;

    /**
     * Clears any previously-set explicit tick values and reverts back to the scale’s tick generator.
     *
     * @param values null
     */
    tickValues(values: null): this;


    /**
     * Returns the currently set tick format function, which defaults to null.
     */
    tickFormat(): ((domainValue: Domain) => string) | null;

    /**
     *  Sets the tick format function and returns the axis.
     *
     * @param format A function mapping a value from the axis Domain to a formatted string
     * for display purposes.
     */
    tickFormat(format: (domainValue: Domain) => string): this;

    /**
     * Reset the tick format function. A null format indicates that the scale’s
     * default formatter should be used, which is generated by calling scale.tickFormat.
     * In this case, the arguments specified by axis.tickArguments
     * are likewise passed to scale.tickFormat.
     *
     * @param format null
     */
    tickFormat(format: null): this;

    /**
     * Get the current inner tick size, which defaults to 6.
     */
    tickSize(): number;
    /**
     * Set the inner and outer tick size to the specified value and return the axis.
     *
     * @param size Tick size in pixels (Default is 6).
     */
    tickSize(size: number): this;

    /**
     * Get the current inner tick size, which defaults to 6.
     * The inner tick size controls the length of the tick lines,
     * offset from the native position of the axis.
     */
    tickSizeInner(): number;

    /**
     * Set the inner tick size to the specified value and return the axis.
     * The inner tick size controls the length of the tick lines,
     * offset from the native position of the axis.
     *
     * @param size Tick size in pixels (Default is 6).
     */
    tickSizeInner(size: number): this;

    /**
     * Get the current outer tick size, which defaults to 6.
     * The outer tick size controls the length of the square ends of the domain path,
     * offset from the native position of the axis. Thus, the “outer ticks” are not actually
     * ticks but part of the domain path, and their position is determined by the associated
     * scale’s domain extent. Thus, outer ticks may overlap with the first or last inner tick.
     * An outer tick size of 0 suppresses the square ends of the domain path,
     * instead producing a straight line.
     */
    tickSizeOuter(): number;

    /**
     * Set the current outer tick size and return the axis.
     * The outer tick size controls the length of the square ends of the domain path,
     * offset from the native position of the axis. Thus, the “outer ticks” are not actually
     * ticks but part of the domain path, and their position is determined by the associated
     * scale’s domain extent. Thus, outer ticks may overlap with the first or last inner tick.
     * An outer tick size of 0 suppresses the square ends of the domain path,
     * instead producing a straight line.
     *
     * @param size Tick size in pixels (Default is 6).
     */
    tickSizeOuter(size: number): this;

    /**
     * Get the current padding, which defaults to 3.
     */
    tickPadding(): number;

    /**
     * Set the current padding and return the axis.
     *
     * @param padding Padding in pixels  (Default is 3).
     */
    tickPadding(padding: number): this;

}

/**
 * Constructs a new top-oriented axis generator for the given scale, with empty tick arguments,
 * a tick size of 6 and padding of 3. In this orientation, ticks are drawn above the horizontal domain path.
 *
 * @param scale The scale to be used for axis generation
 */
export function axisTop<Domain>(scale: AxisScale<Domain>): Axis<Domain>;

/**
 * Constructs a new right-oriented axis generator for the given scale, with empty tick arguments,
 * a tick size of 6 and padding of 3. In this orientation, ticks are drawn to the right of the vertical domain path.
 *
 * @param scale The scale to be used for axis generation
 */
export function axisRight<Domain>(scale: AxisScale<Domain>): Axis<Domain>;

/**
 * Constructs a new bottom-oriented axis generator for the given scale, with empty tick arguments,
 * a tick size of 6 and padding of 3. In this orientation, ticks are drawn below the horizontal domain path.
 *
 * @param scale The scale to be used for axis generation
 */
export function axisBottom<Domain>(scale: AxisScale<Domain>): Axis<Domain>;

/**
 * Constructs a new left-oriented axis generator for the given scale, with empty tick arguments,
 * a tick size of 6 and padding of 3. In this orientation, ticks are drawn to the left of the vertical domain path.
 *
 * @param scale The scale to be used for axis generation
 */
export function axisLeft<Domain>(scale: AxisScale<Domain>): Axis<Domain>;

import { ArrayLike, Selection, TransitionLike, ValueFn } from '../d3-selection';

/**
 * Type alias for a BrushSelection. For a two-dimensional brush, it must be defined as [[x0, y0], [x1, y1]],
 * where x0 is the minimum x-value, y0 is the minimum y-value, x1 is the maximum x-value, and y1 is the maximum y-value.
 * For an x-brush, it must be defined as [x0, x1]; for a y-brush, it must be defined as [y0, y1].
 */
export type BrushSelection = [[number, number], [number, number]] | [number, number];


export interface BrushBehavior<Datum> {
    (group: Selection<SVGGElement, Datum, any, any>, ...args: any[]): void;
    move(group: Selection<SVGGElement, Datum, any, any>, selection: BrushSelection): void;
    move(group: Selection<SVGGElement, Datum, any, any>, selection: ValueFn<SVGGElement, Datum, BrushSelection>): void;
    move(group: TransitionLike<SVGGElement, Datum>, selection: BrushSelection): void;
    move(group: TransitionLike<SVGGElement, Datum>, selection: ValueFn<SVGGElement, Datum, BrushSelection>): void;
    extent(): ValueFn<SVGGElement, Datum, [[number, number], [number, number]]>;
    extent(extent: [[number, number], [number, number]]): this;
    extent(extent: ValueFn<SVGGElement, Datum, [[number, number], [number, number]]>): this;
    filter(): ValueFn<SVGGElement, Datum, boolean>;
    filter(filterFn: ValueFn<SVGGElement, Datum, boolean>): this;
    handleSize(): number;
    handleSize(size: number): this;
    on(typenames: string): ValueFn<SVGGElement, Datum, void>;
    on(typenames: string, callback: null): this;
    on(typenames: string, callback: ValueFn<SVGGElement, Datum, void>): this;

}

export function brush<Datum>(): BrushBehavior<Datum>;
export function brushX<Datum>(): BrushBehavior<Datum>;
export function brushY<Datum>(): BrushBehavior<Datum>;

export function brushSelection(node: SVGGElement): BrushSelection;

export interface D3BrushEvent<Datum> {
    target: BrushBehavior<Datum>;
    type: 'start' | 'brush' | 'end' | string; // Leave failsafe string type for cases like 'brush.foo'
    selection: BrushSelection;
    sourceEvent: any;
}


export interface ChordSubgroup {
    startAngle: number;
    endAngle: number;
    value: number;
    index: number;
    subindex: number;
}

export interface Chord {
    source: ChordSubgroup;
    target: ChordSubgroup;
}

export interface ChordGroup {
    startAngle: number;
    endAngle: number;
    value: number;
    index: number;
}

export interface Chords extends Array<Chord> {
    groups: Array<ChordGroup>;
}

export interface ChordLayout {
    (matrix: number[][]): Chords;
    padAngle(): number;
    padAngle(angle: number): this;
    sortGroups(): ((a: number, b: number) => number) | null;
    sortGroups(compare: null): this;
    sortGroups(compare: (a: number, b: number) => number): this;
    sortSubgroups(): ((a: number, b: number) => number) | null;
    sortSubgroups(compare: null): this;
    sortSubgroups(compare: (a: number, b: number) => number): this;
    sortChords(): ((a: number, b: number) => number) | null;
    sortChords(compare: null): this;
    sortChords(compare: (a: number, b: number) => number): this;
}

export function chord(): ChordLayout;



export interface RibbonGenerator<This, ChordDatum, ChordSubgroupDatum> {
    (this: This, d: ChordDatum, ...args: any[]): string | undefined;
    source(): (this: This, d: ChordDatum, ...args: any[]) => ChordSubgroupDatum;
    source(source: (this: This, d: ChordDatum, ...args: any[]) => ChordSubgroupDatum): this;
    target(): (this: This, d: ChordDatum, ...args: any[]) => ChordSubgroupDatum;
    target(target: (this: This, d: ChordDatum, ...args: any[]) => ChordSubgroupDatum): this;
    radius(): (this: This, d: ChordSubgroupDatum, ...args: any[]) => number;
    radius(radius: number): this;
    radius(radius: (this: This, d: ChordSubgroupDatum, ...args: any[]) => number): this;
    startAngle(): (this: This, d: ChordSubgroupDatum, ...args: any[]) => number;
    startAngle(angle: number): this;
    startAngle(angle: (this: This, d: ChordSubgroupDatum, ...args: any[]) => number): this;
    endAngle(): (this: This, d: ChordSubgroupDatum, ...args: any[]) => number;
    endAngle(angle: number): this;
    endAngle(angle: (this: This, d: ChordSubgroupDatum, ...args: any[]) => number): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;
}

export function ribbon(): RibbonGenerator<any, Chord, ChordSubgroup>;
export function ribbon<Datum, SubgroupDatum>(): RibbonGenerator<any, Datum, SubgroupDatum>;
export function ribbon<This, Datum, SubgroupDatum>(): RibbonGenerator<This, Datum, SubgroupDatum>;

/**
 * Reference type things that can be coerced to string implicitely
 */
type Stringifiable = {
    toString(): string;
};


export function keys(object: { [key: string]: any }): Array<string>;
export function keys(object: Object): Array<string>;

export function values<T>(object: { [key: string]: T }): Array<T>;
export function values(object: Object): Array<any>;

export function entries<T>(object: { [key: string]: T }): Array<{ key: string, value: T }>;
export function entries(object: Object): Array<{ key: string, value: any }>;



export interface Map<T> {
    has(key: string): boolean;
    get(key: string): T | undefined;
    set(key: string, value: T): this;
    remove(key: string): boolean;
    clear(): void;
    keys(): Array<string>;
    values(): Array<T>;
    entries(): Array<{ key: string, value: T }>;
    each(func: (value: T, key: string, map: Map<T>) => void): void;
    empty(): boolean;
    size(): number;
}

export function map<T>(): Map<T>;
export function map<T>(d3Map: Map<T>): Map<T>;
export function map<T>(object: { [key: string]: T }): Map<T>;
export function map<T>(object: { [key: number]: T }): Map<T>;
export function map<T>(array: Array<T>, key?: (value: T, i?: number, array?: Array<T>) => string): Map<T>;
export function map(object: Object): Map<any>;



export interface Set {
    has(value: string | Stringifiable): boolean;
    add(value: string | Stringifiable): this;
    remove(value: string | Stringifiable): boolean;
    clear(): void;
    values(): Array<string>;
    /**
     * The first and second parameter of the function are both passed
     * the 'value' of the set entry for consistency with map.each(...)
     * signature
     */
    each(func: (value: string, valueRepeat: string, set: Set) => void): void;
    empty(): boolean;
    size(): number;
}


export function set(): Set;
export function set(d3Set: Set): Set;
export function set(array: Array<string | Stringifiable>): Set;
export function set<T>(array: Array<T>, key: (value: T, index?: number, array?: Array<T>) => string): Set;




export interface NestedArray<Datum, RollupType> extends Array<{ key: string, values: NestedArray<Datum, RollupType> | Array<Datum> | undefined, value: RollupType | undefined }> { }
export interface NestedMap<Datum, RollupType> extends Map<NestedMap<Datum, RollupType> | Array<Datum> | RollupType> { }
export interface NestedObject<Datum, RollupType> {
    [key: string]: NestedObject<Datum, RollupType> | Array<Datum> | RollupType;
}

interface Nest<Datum, RollupType> {
    key(func: (datum: Datum) => string): this;
    sortKeys(comparator: (a: string, b: string) => number): this;
    sortValues(comparator: (a: Datum, b: Datum) => number): this;
    rollup(func: (values: Datum[]) => RollupType): this;
    map(array: Datum[]): Map<any>; // more specifically it returns NestedMap<Datum, RollupType>
    object(array: Datum[]): { [key: string]: any };  // more specifically it returns NestedObject<Datum, RollupType>
    entries(array: Datum[]): Array<{ key: string; values: any; value: RollupType | undefined }>;  // more specifically it returns NestedArray<Datum, RollupType>
}

export function nest<Datum>(): Nest<Datum, undefined>;
export function nest<Datum, RollupType>(): Nest<Datum, RollupType>;


/**
 * Type allowing for color objects from a specified color space
 */
export type ColorSpaceObject = RGBColor | HSLColor | LabColor | HCLColor | CubehelixColor;

/**
 * A helper interface of methods common to color objects (including colors defined outside the d3-color standard module,
 * e.g. in d3-hsv). This interface
 */
export interface ColorCommonInstance {
    displayable(): boolean;
    toString(): string;
    brighter(k?: number): this;
    darker(k?: number): this;
    rgb(): RGBColor;
}

export interface Color {
    displayable(): boolean; // Note: While this method is used in prototyping for colors of specific colorspaces, it should not be called directly, as 'this.rgb' would not be implemented on Color
    toString(): string; // Note: While this method is used in prototyping for colors of specific colorspaces, it should not be called directly, as 'this.rgb' would not be implemented on Color
}

export interface ColorFactory extends Function {
    (cssColorSpecifier: string): RGBColor | HSLColor;
    (color: ColorSpaceObject | ColorCommonInstance): RGBColor | HSLColor;
    //    prototype: Color;
}

export interface RGBColor extends Color {
    r: number;
    g: number;
    b: number;
    opacity: number;
    brighter(k?: number): this;
    darker(k?: number): this;
    displayable(): boolean;
    rgb(): RGBColor;
    toString(): string;
}

export interface RGBColorFactory extends Function {
    (r: number, g: number, b: number, opacity?: number): RGBColor;
    (cssColorSpecifier: string): RGBColor;
    (color: ColorSpaceObject | ColorCommonInstance): RGBColor;
    //    prototype: RGBColor;
}

export interface HSLColor extends Color {
    h: number;
    s: number;
    l: number;
    opacity: number;
    brighter(k?: number): this;
    darker(k?: number): this;
    displayable(): boolean;
    rgb(): RGBColor;
}

export interface HSLColorFactory extends Function {
    (h: number, s: number, l: number, opacity?: number): HSLColor;
    (cssColorSpecifier: string): HSLColor;
    (color: ColorSpaceObject | ColorCommonInstance): HSLColor;
    //    prototype: HSLColor;
}

export interface LabColor extends Color {
    l: number;
    a: number;
    b: number;
    opacity: number;
    brighter(k?: number): this;
    darker(k?: number): this;
    rgb(): RGBColor;
}

export interface LabColorFactory extends Function {
    (l: number, a: number, b: number, opacity?: number): LabColor;
    (cssColorSpecifier: string): LabColor;
    (color: ColorSpaceObject | ColorCommonInstance): LabColor;
    //    prototype: LabColor;
}

export interface HCLColor extends Color {
    h: number;
    c: number;
    l: number;
    opacity: number;
    brighter(k?: number): this;
    darker(k?: number): this;
    rgb(): RGBColor;
}

export interface HCLColorFactory extends Function {
    (h: number, l: number, c: number, opacity?: number): HCLColor;
    (cssColorSpecifier: string): HCLColor;
    (color: ColorSpaceObject | ColorCommonInstance): HCLColor;
    //    prototype: HCLColor;
}

export interface CubehelixColor extends Color {
    h: number;
    s: number;
    l: number;
    opacity: number;
    brighter(k?: number): this;
    darker(k?: number): this;
    rgb(): RGBColor;
}

export interface CubehelixColorFactory extends Function {
    (h: number, s: number, l: number, opacity?: number): CubehelixColor;
    (cssColorSpecifier: string): CubehelixColor;
    (color: ColorSpaceObject | ColorCommonInstance): CubehelixColor;
    //    prototype: CubehelixColor;
}


export var color: ColorFactory;

export var rgb: RGBColorFactory;

export var hsl: HSLColorFactory;

export var lab: LabColorFactory;

export var hcl: HCLColorFactory;

export var cubehelix: CubehelixColorFactory;

export interface Dispatch<T extends EventTarget> {
    apply(type: string, that?: T, args?: any[]): void;
    call(type: string, that?: T, ...args: any[]): void;
    copy(): Dispatch<T>;

    on(typenames: string): (this: T, ...args: any[]) => void;
    on(typenames: string, callback: null): this;
    on(typenames: string, callback: (this: T, ...args: any[]) => void): this;
}

export function dispatch<T extends EventTarget>(...types: string[]): Dispatch<T>;

import { ArrayLike, Selection, ValueFn } from '../d3-selection';




/**
 * DraggedElementBaseType serves as an alias for the 'minimal' data type which can be selected
 * without 'd3-drag' (and related code in 'd3-selection') trying to use properties internally which would otherwise not
 * be supported.
 */
export type DraggedElementBaseType = Element;


/**
 * Container element type usable for mouse/touch functions
 */
export type DragContainerElement = HTMLElement | SVGSVGElement | SVGGElement; // HTMLElement includes HTMLCanvasElement

/**
 * The subject datum should at a minimum expose x and y properties, so that the relative position
 * of the subject and the pointer can be preserved during the drag gesture.
 */
export interface SubjectPosition {
    x: number;
    y: number;
}

export interface DragBehavior<GElement extends DraggedElementBaseType, Datum, Subject> extends Function {
    (selection: Selection<GElement, Datum, any, any>, ...args: any[]): void;
    container(): ValueFn<GElement, Datum, DragContainerElement>;
    container(accessor: ValueFn<GElement, Datum, DragContainerElement>): this;
    container(container: DragContainerElement): this;
    filter(): ValueFn<GElement, Datum, boolean>;
    filter(filterFn: ValueFn<GElement, Datum, boolean>): this;
    subject(): ValueFn<GElement, Datum, Subject>;
    subject(accessor: ValueFn<GElement, Datum, Subject>): this;
    on(typenames: string): ValueFn<GElement, Datum, void>;
    on(typenames: string, callback: null): this;
    on(typenames: string, callback: ValueFn<GElement, Datum, void>): this;
}

export function drag<GElement extends DraggedElementBaseType, Datum>(): DragBehavior<GElement, Datum, Datum | SubjectPosition>;
export function drag<GElement extends DraggedElementBaseType, Datum, Subject>(): DragBehavior<GElement, Datum, Subject>;


export interface D3DragEvent<GElement extends DraggedElementBaseType, Datum, Subject> {
    target: DragBehavior<GElement, Datum, Subject>;
    type: 'start' | 'drag' | 'end' | string;  // Leave failsafe string type for cases like 'drag.foo'
    subject: Subject;
    x: number;
    y: number;
    dx: number;
    dy: number;
    identifier: 'mouse' | number;
    active: number;
    sourceEvent: any;
    on(typenames: string): ValueFn<GElement, Datum, void>;
    on(typenames: string, callback: null): this;
    on(typenames: string, callback: ValueFn<GElement, Datum, void>): this;
}

export function dragDisable(window: Window): void;

export function dragEnable(window: Window, noClick?: boolean): void;


export interface DSVRowString {
    [key: string]: string;
}

export interface DSVRowAny {
    [key: string]: any;
}

export interface DSVParsedArray<T> extends Array<T> {
    columns: Array<string>;
}



export function csvParse(csvString: string): DSVParsedArray<DSVRowString>;
export function csvParse<ParsedRow extends DSVRowAny>(csvString: string, row: (rawRow: DSVRowString, index: number, columns: Array<string>) => ParsedRow): DSVParsedArray<ParsedRow>;


export function csvParseRows(csvString: string): Array<Array<string>>;
export function csvParseRows<ParsedRow extends DSVRowAny>(csvString: string, row: (rawRow: Array<string>, index: number) => ParsedRow): Array<ParsedRow>;


export function csvFormat(rows: Array<DSVRowAny>): string;
export function csvFormat(rows: Array<DSVRowAny>, columns: Array<string>): string;


export function csvFormatRows(rows: Array<Array<string>>): string;



export function tsvParse(tsvString: string): DSVParsedArray<DSVRowString>;
export function tsvParse<MappedRow extends DSVRowAny>(tsvString: string, row: (rawRow: DSVRowString, index: number, columns: Array<string>) => MappedRow): DSVParsedArray<MappedRow>;


export function tsvParseRows(tsvString: string): Array<Array<string>>;
export function tsvParseRows<MappedRow extends DSVRowAny>(tsvString: string, row: (rawRow: Array<string>, index: number) => MappedRow): Array<MappedRow>;


export function tsvFormat(rows: Array<DSVRowAny>): string;
export function tsvFormat(rows: Array<DSVRowAny>, columns: Array<string>): string;


export function tsvFormatRows(rows: Array<Array<string>>): string;


export interface DSV {
    parse(dsvString: string): DSVParsedArray<DSVRowString>;
    parse<ParsedRow extends DSVRowAny>(dsvString: string, row: (rawRow: DSVRowString, index: number, columns: Array<string>) => ParsedRow): DSVParsedArray<ParsedRow>;
    parseRows(dsvString: string): Array<Array<string>>;
    parseRows<ParsedRow extends DSVRowAny>(dsvString: string, row: (rawRow: Array<string>, index: number) => ParsedRow): Array<ParsedRow>;
    format(rows: Array<DSVRowAny>): string;
    format(rows: Array<DSVRowAny>, columns: Array<string>): string;
    formatRows(rows: Array<Array<string>>): string;
}

export function dsvFormat(delimiter: string): DSV;



export function easeLinear(normalizedTime: number): number;

export function easeQuad(normalizedTime: number): number;
export function easeQuadIn(normalizedTime: number): number;
export function easeQuadOut(normalizedTime: number): number;
export function easeQuadInOut(normalizedTime: number): number;

export function easeCubic(normalizedTime: number): number;
export function easeCubicIn(normalizedTime: number): number;
export function easeCubicOut(normalizedTime: number): number;
export function easeCubicInOut(normalizedTime: number): number;

export function easePoly(normalizedTime: number): number;
export function easePolyIn(normalizedTime: number): number;
export function easePolyOut(normalizedTime: number): number;
export function easePolyInOut(normalizedTime: number): number;

export function easeSin(normalizedTime: number): number;
export function easeSinIn(normalizedTime: number): number;
export function easeSinOut(normalizedTime: number): number;
export function easeSinInOut(normalizedTime: number): number;

export function easeExp(normalizedTime: number): number;
export function easeExpIn(normalizedTime: number): number;
export function easeExpOut(normalizedTime: number): number;
export function easeExpInOut(normalizedTime: number): number;

export function easeCircle(normalizedTime: number): number;
export function easeCircleIn(normalizedTime: number): number;
export function easeCircleOut(normalizedTime: number): number;
export function easeCircleInOut(normalizedTime: number): number;

export function easeBounce(normalizedTime: number): number;
export function easeBounceIn(normalizedTime: number): number;
export function easeBounceOut(normalizedTime: number): number;
export function easeBounceInOut(normalizedTime: number): number;

export function easeBack(normalizedTime: number): number;
export function easeBackIn(normalizedTime: number): number;
export function easeBackOut(normalizedTime: number): number;
export function easeBackInOut(normalizedTime: number): number;

export function easeElastic(normalizedTime: number): number;
export function easeElasticIn(normalizedTime: number): number;
export function easeElasticOut(normalizedTime: number): number;
export function easeElasticInOut(normalizedTime: number): number;



export interface SimulationNodeDatum {
    // NB: index is assigned internally by simulation, once initialized it is defined
    index?: number;
    x?: number;
    y?: number;
    vx?: number;
    vy?: number;
    fx?: number;
    fy?: number;
}

export interface SimulationLinkDatum<NodeDatum extends SimulationNodeDatum> {
    // TODO: Strictly speaking, the string or number typing of source and target is only used when (re)initializing links
    // Once initialized, links' source and target fields will be of type NodeDatum
    source: NodeDatum | string | number;
    target: NodeDatum | string | number;
    // NB: index is assigned internally by force, once initialized it is defined
    index?: number;
}

export interface Simulation<NodeDatum extends SimulationNodeDatum, LinkDatum extends SimulationLinkDatum<NodeDatum>> {
    restart(): this;
    stop(): this;
    tick(): void;
    nodes(): Array<NodeDatum>;
    nodes(nodesData: Array<NodeDatum>): this;
    alpha(): number;
    alpha(alpha: number): this;
    alphaMin(): number;
    alphaMin(min: number): this;
    alphaDecay(): number;
    alphaDecay(decay: number): this;
    alphaTarget(): number;
    alphaTarget(target: number): this;
    velocityDecay(): number;
    velocityDecay(decay: number): this;
    force<F extends Force<NodeDatum, LinkDatum>>(name: string): F; // force names are arbitrary, so return type inference is not possible
    force(name: string, force: null): this;
    force(name: string, force: Force<NodeDatum, LinkDatum>): this;
    find(x: number, y: number, radius?: number): NodeDatum | undefined;
    on(typenames: 'tick' | 'end' | string): (this: Simulation<NodeDatum, LinkDatum>) => void;
    on(typenames: 'tick' | 'end' | string, listener: null): this;
    on(typenames: 'tick' | 'end' | string, listener: (this: this) => void): this;
}

export function forceSimulation<NodeDatum extends SimulationNodeDatum>(nodesData?: Array<NodeDatum>): Simulation<NodeDatum, undefined>;
export function forceSimulation<NodeDatum extends SimulationNodeDatum, LinkDatum extends SimulationLinkDatum<NodeDatum>>(nodesData?: Array<NodeDatum>): Simulation<NodeDatum, LinkDatum>;



export interface Force<NodeDatum extends SimulationNodeDatum, LinkDatum extends SimulationLinkDatum<NodeDatum>> {
    (alpha: number): void;
    initialize?(nodes: Array<NodeDatum>): void;
}



export interface ForceCenter<NodeDatum extends SimulationNodeDatum> extends Force<NodeDatum, any> {
    x(): number;
    x(x: number): this;
    y(): number;
    y(y: number): this;
}

export function forceCenter<NodeDatum extends SimulationNodeDatum>(x?: number, y?: number): ForceCenter<NodeDatum>;


export interface ForceCollide<NodeDatum extends SimulationNodeDatum> extends Force<NodeDatum, any> {
    radius(): (node: NodeDatum, i: number, nodes: Array<NodeDatum>) => number;
    radius(radius: number): this;
    radius(radius: (node: NodeDatum, i: number, nodes: Array<NodeDatum>) => number): this;
    strength(): number;
    strength(strength: number): this;
    iterations(): number;
    iterations(iterations: number): this;
}

export function forceCollide<NodeDatum extends SimulationNodeDatum>(): ForceCollide<NodeDatum>;
export function forceCollide<NodeDatum extends SimulationNodeDatum>(radius: number): ForceCollide<NodeDatum>;
export function forceCollide<NodeDatum extends SimulationNodeDatum>(radius: (node: NodeDatum, i: number, nodes: Array<NodeDatum>) => number): ForceCollide<NodeDatum>;


export interface ForceLink<NodeDatum extends SimulationNodeDatum, LinkDatum extends SimulationLinkDatum<NodeDatum>> extends Force<NodeDatum, LinkDatum> {
    links(): Array<LinkDatum>;
    links(links: Array<LinkDatum>): this;
    id(): (node: NodeDatum, i: number, nodesData: Array<NodeDatum>) => (string | number);
    id(id: (node: NodeDatum, i: number, nodesData: Array<NodeDatum>) => string): this;
    distance(): (link: LinkDatum, i: number, links: Array<LinkDatum>) => number;
    distance(distance: number): this;
    distance(distance: (link: LinkDatum, i: number, links: Array<LinkDatum>) => number): this;
    strength(): (link: LinkDatum, i: number, links: Array<LinkDatum>) => number;
    strength(strength: number): this;
    strength(strength: (link: LinkDatum, i: number, links: Array<LinkDatum>) => number): this;
    iterations(): number;
    iterations(iterations: number): this;
}

export function forceLink<NodeDatum extends SimulationNodeDatum, LinksDatum extends SimulationLinkDatum<NodeDatum>>(): ForceLink<NodeDatum, LinksDatum>;
export function forceLink<NodeDatum extends SimulationNodeDatum, LinksDatum extends SimulationLinkDatum<NodeDatum>>(links: Array<LinksDatum>): ForceLink<NodeDatum, LinksDatum>;


export interface ForceManyBody<NodeDatum extends SimulationNodeDatum> extends Force<NodeDatum, any> {
    strength(): (d: NodeDatum, i: number, data: Array<NodeDatum>) => number;
    strength(strength: number): this;
    strength(strength: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): this;
    theta(): number;
    theta(theta: number): this;
    distanceMin(): number;
    distanceMin(distance: number): this;
    distanceMax(): number;
    distanceMax(distance: number): this;
}

export function forceManyBody<NodeDatum extends SimulationNodeDatum>(): ForceManyBody<NodeDatum>;


export interface ForceX<NodeDatum extends SimulationNodeDatum> extends Force<NodeDatum, any> {
    strength(): (d: NodeDatum, i: number, data: Array<NodeDatum>) => number;
    strength(strength: number): this;
    strength(strength: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): this;
    x(): (d: NodeDatum, i: number, data: Array<NodeDatum>) => number;
    x(x: number): this;
    x(x: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): this;
}

export function forceX<NodeDatum extends SimulationNodeDatum>(): ForceX<NodeDatum>;
export function forceX<NodeDatum extends SimulationNodeDatum>(x: number): ForceX<NodeDatum>;
export function forceX<NodeDatum extends SimulationNodeDatum>(x: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): ForceX<NodeDatum>;

export interface ForceY<NodeDatum extends SimulationNodeDatum> extends Force<NodeDatum, any> {
    strength(): (d: NodeDatum, i: number, data: Array<NodeDatum>) => number;
    strength(strength: number): this;
    strength(strength: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): this;
    y(): (d: NodeDatum, i: number, data: Array<NodeDatum>) => number;
    y(y: number): this;
    y(y: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): this;
}

export function forceY<NodeDatum extends SimulationNodeDatum>(): ForceY<NodeDatum>;
export function forceY<NodeDatum extends SimulationNodeDatum>(y: number): ForceY<NodeDatum>;
export function forceY<NodeDatum extends SimulationNodeDatum>(y: (d: NodeDatum, i: number, data: Array<NodeDatum>) => number): ForceY<NodeDatum>;

/**
 * Specification of locale to use when creating a new FormatLocaleObject
 */
export interface FormatLocaleDefinition {
    /**
     * The decimal point (e.g., ".")
     */
    decimal: '.' | ',';
    /**
     * The group separator (e.g., ","). Note that the thousands property is a misnomer, as\
     * the grouping definition allows groups other than thousands.
     */
    thousands: '.' | ',';
    /**
     * The array of group sizes (e.g., [3]), cycled as needed.
     */
    grouping: number[];
    /**
     * The currency prefix and suffix (e.g., ["$", ""])
     */
    currency: [string, string];
}


export interface FormatLocaleObject {

    /**
     * Returns a new format function for the given string specifier. The returned function
     * takes a number as the only argument, and returns a string representing the formatted number.
     *
     * @param specifier A Specifier string
     */
    format(specifier: string): (n: number) => string;

    /**
     * Returns a new format function for the given string specifier. The returned function
     * takes a number as the only argument, and returns a string representing the formatted number.
     * The returned function will convert values to the units of the appropriate SI prefix for the
     * specified numeric reference value before formatting in fixed point notation.
     *
     * @param specifier A Specifier string
     * @param value The reference value to determine the appropriate SI prefix.
     */
    formatPrefix(specifier: string, value: number): (n: number) => string;
}


export interface FormatSpecifier {
    fill: string;
    align: string;
    sign: string;
    symbol: string;
    zero: boolean;
    width: number;
    comma: boolean;
    precision: number;
    type: string;
    toString(): string;
}

/**
 * Create a new locale-based object which exposes format(...) and formatPrefix(...)
 * methods for the specified locale.
 */
export function formatLocale(locale: FormatLocaleDefinition): FormatLocaleObject;

/**
 * Create a new locale-based object which exposes format(...) and formatPrefix(...)
 * methods for the specified locale definition. The specified locale definition will be
 * set as the new default locale definition.
 */
export function formatDefaultLocale(defaultLocale: FormatLocaleDefinition): FormatLocaleObject;

/**
 * Returns a new format function for the given string specifier. The returned function
 * takes a number as the only argument, and returns a string representing the formatted number.
 *
 * Uses the current default locale.
 *
 * @param specifier A Specifier string
 */
export function format(specifier: string): (n: number) => string;

/**
 * Returns a new format function for the given string specifier. The returned function
 * takes a number as the only argument, and returns a string representing the formatted number.
 * The returned function will convert values to the units of the appropriate SI prefix for the
 * specified numeric reference value before formatting in fixed point notation.
 *
 *  Uses the current default locale.
 *
 * @param specifier A Specifier string
 * @param value The reference value to determine the appropriate SI prefix.
 */
export function formatPrefix(specifier: string, value: number): (n: number) => string;

/**
 * Parses the specified specifier, returning an object with exposed fields that correspond to the
 * format specification mini-language and a toString method that reconstructs the specifier.
 *
 * @param specifier A specifier string.
 */
export function formatSpecifier(specifier: string): FormatSpecifier;

/**
 * Returns a suggested decimal precision for fixed point notation given the specified numeric step value.
 *
 * @param step The step represents the minimum absolute difference between values that will be formatted.
 * (This assumes that the values to be formatted are also multiples of step.)
 */
export function precisionFixed(step: number): number;

/**
 * Returns a suggested decimal precision for use with locale.formatPrefix given the specified
 * numeric step and reference value.
 *
 * @param step The step represents the minimum absolute difference between values that will be formatted.
 * (This assumes that the values to be formatted are also multiples of step.)
 * @param value Reference value determines which SI prefix will be used.
 */
export function precisionPrefix(step: number, value: number): number;


/**
 * Returns a suggested decimal precision for format types that round to significant digits
 * given the specified numeric step and max values.
 *
 * @param step The step represents the minimum absolute difference between values that will be formatted.
 * (This assumes that the values to be formatted are also multiples of step.)
 * @param max max represents the largest absolute value that will be formatted.
 */
export function precisionRound(step: number, max: number): number;



/**
 * A basic geometry for a sphere, which is supported by d3-geo
 * beyond the GeoJSON geometries.
 */
export interface GeoSphere {
    type: 'Sphere';
}

/**
 * Type Alias for GeoJSON Geometry Object and GeoSphere additional
 * geometry supported by d3-geo
 */
export type GeoGeometryObjects = GeoJSON.GeometryObject | GeoSphere;

/**
 * A GeoJSON-style GeometryCollection which supports GeoJSON geometry objects
 * and additionally GeoSphere
 */
export interface ExtendedGeometryCollection<GeometryType extends GeoGeometryObjects> {
    type: string;
    bbox?: number[];
    crs?: GeoJSON.CoordinateReferenceSystem;
    geometries: GeometryType[];
}

/**
 * A GeoJSON-style Feature which support features built on GeoJSON GeometryObjects
 * or GeoSphere
 */
export interface ExtendedFeature<GeometryType extends GeoGeometryObjects, Properties> extends GeoJSON.GeoJsonObject {
    geometry: GeometryType;
    properties: Properties;
    id?: string;
}

/**
 * A GeoJSON-style FeatureCollection which supports GeoJSON features
 * and features built on GeoSphere
 */
export interface ExtendedFeatureCollection<FeatureType extends ExtendedFeature<GeoGeometryObjects, any>> extends GeoJSON.GeoJsonObject {
    features: FeatureType[];
}

/**
 * Type Alias for permissible objects which can be used with d3-geo
 * methods
 */
export type GeoPermissibleObjects = GeoGeometryObjects | ExtendedGeometryCollection<GeoGeometryObjects> | ExtendedFeature<GeoGeometryObjects, any> | ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>;


/**Returns the spherical area of the specified GeoJSON feature in steradians. */
export function geoArea(feature: ExtendedFeature<GeoGeometryObjects, any>): number;
export function geoArea(feature: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>): number;
export function geoArea(feature: GeoGeometryObjects): number;
export function geoArea(feature: ExtendedGeometryCollection<GeoGeometryObjects>): number;

/**Returns the spherical bounding box for the specified GeoJSON feature. The bounding box is represented by a two-dimensional array: [[left, bottom], [right, top]], where left is the minimum longitude, bottom is the minimum latitude, right is maximum longitude, and top is the maximum latitude. All coordinates are given in degrees. */
export function geoBounds(feature: ExtendedFeature<GeoGeometryObjects, any>): [[number, number], [number, number]];
export function geoBounds(feature: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>): [[number, number], [number, number]];
export function geoBounds(feature: GeoGeometryObjects): [[number, number], [number, number]];
export function geoBounds(feature: ExtendedGeometryCollection<GeoGeometryObjects>): [[number, number], [number, number]];

/**Returns the spherical centroid of the specified GeoJSON feature. See also path.centroid, which computes the projected planar centroid.*/
export function geoCentroid(feature: ExtendedFeature<GeoGeometryObjects, any>): [number, number];
export function geoCentroid(feature: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>): [number, number];
export function geoCentroid(feature: GeoGeometryObjects): [number, number];
export function geoCentroid(feature: ExtendedGeometryCollection<GeoGeometryObjects>): [number, number];

/**Returns the great-arc distance in radians between the two points a and b. Each point must be specified as a two-element array [longitude, latitude] in degrees. */
export function geoDistance(a: [number, number], b: [number, number]): number;

/**Returns the great-arc length of the specified GeoJSON feature in radians.*/
export function geoLength(feature: ExtendedFeature<GeoGeometryObjects, any>): number;
export function geoLength(feature: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>): number;
export function geoLength(feature: GeoGeometryObjects): number;
export function geoLength(feature: ExtendedGeometryCollection<GeoGeometryObjects>): number;

/**Returns an interpolator function given two points a and b. Each point must be specified as a two-element array [longitude, latitude] in degrees. */
export function geoInterpolate(a: [number, number], b: [number, number]): (t: number) => [number, number];


export interface GeoRotation {
    (point: [number, number]): [number, number];
    invert(point: [number, number]): [number, number];
}

/**Returns a rotation function for the given angles, which must be a two- or three-element array of numbers [lambda, phi, gamma] specifying the rotation angles in degrees about each spherical axis. */
export function geoRotation(angles: [number, number] | [number, number, number]): GeoRotation;




export interface GeoCircleGenerator<This, Datum> {
    /**Returns a new GeoJSON geometry object of type “Polygon” approximating a circle on the surface of a sphere, with the current center, radius and precision. */
    (this: This, d?: Datum, ...args: any[]): GeoJSON.Polygon;
    center(): ((this: This, d: Datum, ...args: any[]) => [number, number]);
    center(center: [number, number]): this;
    center(center: ((this: This, d: Datum, ...args: any[]) => [number, number])): this;

    radius(): ((this: This, d: Datum, ...args: any[]) => number);
    radius(radius: number): this;
    radius(radius: ((this: This, d: Datum, ...args: any[]) => number)): this;

    precision(): ((this: This, d: Datum, ...args: any[]) => number);
    precision(precision: number): this;
    precision(precision: (this: This, d: Datum, ...args: any[]) => number): this;
}

export function geoCircle(): GeoCircleGenerator<any, any>;
export function geoCircle<Datum>(): GeoCircleGenerator<any, Datum>;
export function geoCircle<This, Datum>(): GeoCircleGenerator<This, Datum>;


export interface GeoGraticuleGenerator {
    /**Returns a GeoJSON MultiLineString geometry object representing all meridians and parallels for this graticule. */
    (): GeoJSON.MultiLineString;

    lines(): GeoJSON.LineString[];
    outline(): GeoJSON.Polygon;
    extent(): [[number, number], [number, number]];
    extent(extent: [[number, number], [number, number]]): this;
    extentMajor(): [[number, number], [number, number]];
    extentMajor(extent: [[number, number], [number, number]]): this;
    extentMinor(): [[number, number], [number, number]];
    extentMinor(extent: [[number, number], [number, number]]): this;
    step(): [number, number];
    step(step: [number, number]): this;
    stepMajor(): [number, number];
    stepMajor(step: [number, number]): this;
    stepMinor(): [number, number];
    stepMinor(step: [number, number]): this;
    precision(): number;
    precision(angle: number): this;
}

export function geoGraticule(): GeoGraticuleGenerator;


export interface GeoStream {
    lineEnd(): void;
    lineStart(): void;
    point(x: number, y: number, z?: number): void;
    polygonEnd(): void;
    polygonStart(): void;
    sphere?(): void;
}

export interface GeoStreamWrapper {
    stream(stream: GeoStream): GeoStream;
}


export interface GeoRawProjection {
    (longitude: number, latitude: number): [number, number];
    invert?(x: number, y: number): [number, number];
}


export interface GeoProjection extends GeoStreamWrapper {
    /**Returns a new array x, y representing the projected point of the given point. The point must be specified as a two-element array [longitude, latitude] in degrees. */
    (point: [number, number]): [number, number] | null;

    center(): [number, number];
    center(point: [number, number]): this;

    clipAngle(): number | null;
    clipAngle(angle: null): this;
    clipAngle(angle: number): this;

    clipExtent(): [[number, number], [number, number]] | null;
    clipExtent(extent: null): this;
    clipExtent(extent: [[number, number], [number, number]]): this;

    /**Sets the projection’s scale and translate to fit the specified GeoJSON object in the center of the given extent. */
    fitExtent(extent: [[number, number], [number, number]], object: ExtendedFeature<GeoGeometryObjects, any>): this;
    fitExtent(extent: [[number, number], [number, number]], object: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>): this;
    fitExtent(extent: [[number, number], [number, number]], object: GeoGeometryObjects): this;
    fitExtent(extent: [[number, number], [number, number]], object: ExtendedGeometryCollection<GeoGeometryObjects>): this;


    /**A convenience method for projection.fitExtent where the top-left corner of the extent is [0,0]. */
    fitSize(size: [number, number], object: ExtendedFeature<GeoGeometryObjects, any>): this;
    fitSize(size: [number, number], object: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>): this;
    fitSize(size: [number, number], object: GeoGeometryObjects): this;
    fitSize(size: [number, number], object: ExtendedGeometryCollection<GeoGeometryObjects>): this;

    /**Returns a new array [longitude, latitude] in degrees representing the unprojected point of the given projected point. */
    invert?(point: [number, number]): [number, number] | null;

    precision(): number;
    precision(precision: number): this;

    rotate(): [number, number, number];
    rotate(angles: [number, number] | [number, number, number]): this;

    scale(): number;
    scale(scale: number): this;

    translate(): [number, number];
    translate(point: [number, number]): this;
}

export interface GeoConicProjection extends GeoProjection {
    parallels(value: [number, number]): this;
    parallels(): [number, number];
}



export interface GeoContext {
    arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void;
    beginPath(): void;
    closePath(): void;
    lineTo(x: number, y: number): void;
    moveTo(x: number, y: number): void;
}

export interface GeoPath<This, DatumObject extends GeoPermissibleObjects> {

    (this: This, object: DatumObject, ...args: any[]): string;

    area(object: DatumObject): number;
    bounds(object: DatumObject): [[number, number], [number, number]];
    centroid(object: DatumObject): [number, number];
    context<C extends GeoContext>(): C | null;
    context(context: GeoContext | null): this;

    /**
     * Get the current projection. The generic parameter can be used to cast the result to the
     * correct, known type of the projection, e.g. GeoProjection or GeoConicProjection. Otherwise,
     * the return type defaults to the minimum type requirement for a projection which
     * can be passed into a GeoPath.
     */
    projection<P extends GeoConicProjection | GeoProjection | GeoStreamWrapper>(): P | null;

    /**
     * Set the projection to the identity projection
     */
    projection(projection: null): this;

    /**
     * Set the projection to be used with the geo path generator.
     */
    projection(projection: GeoProjection): this;

    /**
     * Set the projection to be used with the geo path generator to a custom projection.
     * Custom projections must minimally contain a stream method.
     */
    projection(projection: GeoStreamWrapper): this;

    pointRadius(): (this: This, object: DatumObject, ...args: any[]) => number;
    pointRadius(value: number): this;
    pointRadius(value: (this: This, object: DatumObject, ...args: any[]) => number): this;

}

export function geoPath(): GeoPath<any, GeoPermissibleObjects>;
export function geoPath<DatumObject extends GeoPermissibleObjects>(): GeoPath<any, DatumObject>;
export function geoPath<This, DatumObject extends GeoPermissibleObjects>(): GeoPath<This, DatumObject>;


export function geoAzimuthalEqualAreaRaw(): GeoRawProjection;
export function geoAzimuthalEquidistantRaw(): GeoRawProjection;
export function geoConicConformalRaw(phi0: number, phi1: number): GeoRawProjection;
export function geoConicEqualAreaRaw(phi0: number, phi1: number): GeoRawProjection;
export function geoConicEquidistantRaw(phi0: number, phi1: number): GeoRawProjection;
export function geoEquirectangularRaw(): GeoRawProjection;
export function geoGnomonicRaw(): GeoRawProjection;
export function geoMercatorRaw(): GeoRawProjection;
export function geoOrthographicRaw(): GeoRawProjection;
export function geoStereographicRaw(): GeoRawProjection;
export function geoTransverseMercatorRaw(): GeoRawProjection;


export function geoProjection(project: GeoRawProjection): GeoProjection;


export function geoProjectionMutator(factory: (...args: any[]) => GeoRawProjection): () => GeoProjection;


export function geoAlbers(): GeoConicProjection;
export function geoAlbersUsa(): GeoProjection;
export function geoAzimuthalEqualArea(): GeoProjection;
export function geoAzimuthalEquidistant(): GeoProjection;
export function geoConicConformal(): GeoConicProjection;
export function geoConicEqualArea(): GeoConicProjection;
export function geoConicEquidistant(): GeoConicProjection;
export function geoEquirectangular(): GeoProjection;
export function geoGnomonic(): GeoProjection;
export function geoMercator(): GeoProjection;
export function geoOrthographic(): GeoProjection;
export function geoStereographic(): GeoProjection;
export function geoTransverseMercator(): GeoProjection;


export interface GeoExtent {
    extent(): [[number, number], [number, number]];
    extent(extent: [[number, number], [number, number]]): this;
    stream(stream: GeoStream): GeoStream;
}


export function geoClipExtent(): GeoExtent;



export interface GeoTransformPrototype {
    lineEnd?(this: this & { stream: GeoStream }): void;
    lineStart?(this: this & { stream: GeoStream }): void;
    point?(this: this & { stream: GeoStream }, x: number, y: number, z?: number): void;
    polygonEnd?(this: this & { stream: GeoStream }): void;
    polygonStart?(this: this & { stream: GeoStream }): void;
    sphere?(this: this & { stream: GeoStream }): void;
}
export function geoTransform<T extends GeoTransformPrototype>(prototype: T): { stream: (s: GeoStream) => (T & GeoStream) };


export function geoStream(object: ExtendedFeature<GeoGeometryObjects, any>, stream: GeoStream): void;
export function geoStream(object: ExtendedFeatureCollection<ExtendedFeature<GeoGeometryObjects, any>>, stream: GeoStream): void;
export function geoStream(object: GeoGeometryObjects, stream: GeoStream): void;
export function geoStream(object: ExtendedGeometryCollection<GeoGeometryObjects>, stream: GeoStream): void;



export interface HierarchyLink<Datum> {
    source: HierarchyNode<Datum>;
    target: HierarchyNode<Datum>;
}

export interface HierarchyNode<Datum> {
    data: Datum;
    readonly depth: number;
    readonly height: number;
    parent: HierarchyNode<Datum> | null;
    children?: Array<HierarchyNode<Datum>>;
    /**
     * Aggregated numeric value as calculated by sum(value),
     * if previously invoked.
     */
    readonly value?: number;
    /**
     * Optional Node Id string set by StratifyOperator, if
     * hierarchical data was created from tabular data using stratify()
     */
    readonly id?: string;
    ancestors(): Array<HierarchyNode<Datum>>;
    descendants(): Array<HierarchyNode<Datum>>;
    leaves(): Array<HierarchyNode<Datum>>;
    path(target: HierarchyNode<Datum>): Array<HierarchyNode<Datum>>;
    links(): Array<HierarchyLink<Datum>>;
    sum(value: (d: Datum) => number): this;
    sort(compare: (a: HierarchyNode<Datum>, b: HierarchyNode<Datum>) => number): this;
    each(func: (node: HierarchyNode<Datum>) => void): this;
    eachAfter(func: (node: HierarchyNode<Datum>) => void): this;
    eachBefore(func: (node: HierarchyNode<Datum>) => void): this;
    copy(): HierarchyNode<Datum>;
}


export function hierarchy<Datum>(data: Datum, children?: (d: Datum) => (Array<Datum> | null)): HierarchyNode<Datum>;




export interface StratifyOperator<Datum> {
    (data: Array<Datum>): HierarchyNode<Datum>;
    id(): (d: Datum, i: number, data: Array<Datum>) => (string | null | '' | undefined);
    id(id: (d: Datum, i?: number, data?: Array<Datum>) => (string | null | '' | undefined)): this;
    parentId(): (d: Datum, i: number, data: Array<Datum>) => (string | null | '' | undefined);
    parentId(parentId: (d: Datum, i?: number, data?: Array<Datum>) => (string | null | '' | undefined)): this;
}

export function stratify<Datum>(): StratifyOperator<Datum>;


export interface HierarchyPointLink<Datum> {
    source: HierarchyPointNode<Datum>;
    target: HierarchyPointNode<Datum>;
}

export interface HierarchyPointNode<Datum> {
    x: number;
    y: number;
    data: Datum;
    readonly depth: number;
    readonly height: number;
    parent: HierarchyPointNode<Datum> | null;
    children?: Array<HierarchyPointNode<Datum>>;
    /**
     * Aggregated numeric value as calculated by sum(value),
     * if previously invoked.
     */
    readonly value?: number;
    /**
     * Optional Node Id string set by StratifyOperator, if
     * hierarchical data was created from tabular data using stratify()
     */
    readonly id?: string;
    ancestors(): Array<HierarchyPointNode<Datum>>;
    descendants(): Array<HierarchyPointNode<Datum>>;
    leaves(): Array<HierarchyPointNode<Datum>>;
    path(target: HierarchyPointNode<Datum>): Array<HierarchyPointNode<Datum>>;
    links(): Array<HierarchyPointLink<Datum>>;
    sum(value: (d: Datum) => number): this;
    sort(compare: (a: HierarchyPointNode<Datum>, b: HierarchyPointNode<Datum>) => number): this;
    each(func: (node: HierarchyPointNode<Datum>) => void): this;
    eachAfter(func: (node: HierarchyPointNode<Datum>) => void): this;
    eachBefore(func: (node: HierarchyPointNode<Datum>) => void): this;
    copy(): HierarchyPointNode<Datum>;
}

export interface ClusterLayout<Datum> {
    (root: HierarchyNode<Datum>): HierarchyPointNode<Datum>;
    size(): [number, number] | null;
    size(size: [number, number]): this;
    nodeSize(): [number, number] | null;
    nodeSize(size: [number, number]): this;
    separation(): (a: HierarchyPointNode<Datum>, b: HierarchyPointNode<Datum>) => number;
    separation(separation: (a: HierarchyPointNode<Datum>, b: HierarchyPointNode<Datum>) => number): this;
}

export function cluster<Datum>(): ClusterLayout<Datum>;


export interface TreeLayout<Datum> {
    (root: HierarchyNode<Datum>): HierarchyPointNode<Datum>;
    size(): [number, number] | null;
    size(size: [number, number]): this;
    nodeSize(): [number, number] | null;
    nodeSize(size: [number, number]): this;
    separation(): (a: HierarchyPointNode<Datum>, b: HierarchyPointNode<Datum>) => number;
    separation(separation: (a: HierarchyPointNode<Datum>, b: HierarchyPointNode<Datum>) => number): this;
}

export function tree<Datum>(): TreeLayout<Datum>;


export interface HierarchyRectangularLink<Datum> {
    source: HierarchyRectangularNode<Datum>;
    target: HierarchyRectangularNode<Datum>;
}

export interface HierarchyRectangularNode<Datum> {
    x0: number;
    y0: number;
    x1: number;
    y1: number;
    data: Datum;
    readonly depth: number;
    readonly height: number;
    parent: HierarchyRectangularNode<Datum> | null;
    children?: Array<HierarchyRectangularNode<Datum>>;
    /**
     * Aggregated numeric value as calculated by sum(value),
     * if previously invoked.
     */
    readonly value?: number;
    /**
     * Optional Node Id string set by StratifyOperator, if
     * hierarchical data was created from tabular data using stratify()
     */
    readonly id?: string;
    ancestors(): Array<HierarchyRectangularNode<Datum>>;
    descendants(): Array<HierarchyRectangularNode<Datum>>;
    leaves(): Array<HierarchyRectangularNode<Datum>>;
    path(target: HierarchyRectangularNode<Datum>): Array<HierarchyRectangularNode<Datum>>;
    links(): Array<HierarchyRectangularLink<Datum>>;
    sum(value: (d: Datum) => number): this;
    sort(compare: (a: HierarchyRectangularNode<Datum>, b: HierarchyRectangularNode<Datum>) => number): this;
    each(func: (node: HierarchyRectangularNode<Datum>) => void): this;
    eachAfter(func: (node: HierarchyRectangularNode<Datum>) => void): this;
    eachBefore(func: (node: HierarchyRectangularNode<Datum>) => void): this;
    copy(): HierarchyRectangularNode<Datum>;
}

export interface TreemapLayout<Datum> {
    (root: HierarchyNode<Datum>): HierarchyRectangularNode<Datum>;
    tile(): (node: HierarchyRectangularNode<Datum>, x0: number, y0: number, x1: number, y1: number) => void;
    tile(tile: (node: HierarchyRectangularNode<Datum>, x0: number, y0: number, x1: number, y1: number) => void): this;
    size(): [number, number];
    size(size: [number, number]): this;
    round(): boolean;
    round(round: boolean): this;
    padding(): (node: HierarchyRectangularNode<Datum>) => number;
    padding(padding: number): this;
    padding(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
    paddingInner(): (node: HierarchyRectangularNode<Datum>) => number;
    paddingInner(padding: number): this;
    paddingInner(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
    paddingOuter(): (node: HierarchyRectangularNode<Datum>) => number;
    paddingOuter(padding: number): this;
    paddingOuter(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
    paddingTop(): (node: HierarchyRectangularNode<Datum>) => number;
    paddingTop(padding: number): this;
    paddingTop(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
    paddingRight(): (node: HierarchyRectangularNode<Datum>) => number;
    paddingRight(padding: number): this;
    paddingRight(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
    paddingBottom(): (node: HierarchyRectangularNode<Datum>) => number;
    paddingBottom(padding: number): this;
    paddingBottom(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
    paddingLeft(): (node: HierarchyRectangularNode<Datum>) => number;
    paddingLeft(padding: number): this;
    paddingLeft(padding: (node: HierarchyRectangularNode<Datum>) => number): this;
}

export function treemap<Datum>(): TreemapLayout<Datum>;



export function treemapBinary(node: HierarchyRectangularNode<any>, x0: number, y0: number, x1: number, y1: number): void;
export function treemapDice(node: HierarchyRectangularNode<any>, x0: number, y0: number, x1: number, y1: number): void;
export function treemapSlice(node: HierarchyRectangularNode<any>, x0: number, y0: number, x1: number, y1: number): void;
export function treemapSliceDice(node: HierarchyRectangularNode<any>, x0: number, y0: number, x1: number, y1: number): void;

export interface RatioSquarifyTilingFactory {
    (node: HierarchyRectangularNode<any>, x0: number, y0: number, x1: number, y1: number): void;
    ratio(ratio: number): RatioSquarifyTilingFactory;
}

export var treemapSquarify: RatioSquarifyTilingFactory;
export var treemapResquarify: RatioSquarifyTilingFactory;



export interface PartitionLayout<Datum> {
    (root: HierarchyNode<Datum>): HierarchyRectangularNode<Datum>;
    size(): [number, number];
    size(size: [number, number]): this;
    round(): boolean;
    round(round: boolean): this;
    padding(): number;
    padding(padding: number): this;
}

export function partition<Datum>(): PartitionLayout<Datum>;


export interface HierarchyCircularLink<Datum> {
    source: HierarchyCircularNode<Datum>;
    target: HierarchyCircularNode<Datum>;
}

export interface HierarchyCircularNode<Datum> {
    x: number;
    y: number;
    r: number;
    data: Datum;
    readonly depth: number;
    readonly height: number;
    parent: HierarchyCircularNode<Datum> | null;
    children?: Array<HierarchyCircularNode<Datum>>;
    /**
     * Aggregated numeric value as calculated by sum(value),
     * if previously invoked.
     */
    readonly value?: number;
    /**
     * Optional Node Id string set by StratifyOperator, if
     * hierarchical data was created from tabular data using stratify()
     */
    readonly id?: string;
    ancestors(): Array<HierarchyCircularNode<Datum>>;
    descendants(): Array<HierarchyCircularNode<Datum>>;
    leaves(): Array<HierarchyCircularNode<Datum>>;
    path(target: HierarchyCircularNode<Datum>): Array<HierarchyCircularNode<Datum>>;
    links(): Array<HierarchyCircularLink<Datum>>;
    sum(value: (d: Datum) => number): this;
    sort(compare: (a: HierarchyCircularNode<Datum>, b: HierarchyCircularNode<Datum>) => number): this;
    each(func: (node: HierarchyCircularNode<Datum>) => void): this;
    eachAfter(func: (node: HierarchyCircularNode<Datum>) => void): this;
    eachBefore(func: (node: HierarchyCircularNode<Datum>) => void): this;
    copy(): HierarchyCircularNode<Datum>;
}


export interface PackLayout<Datum> {
    (root: HierarchyNode<Datum>): HierarchyCircularNode<Datum>;
    radius(): null | ((node: HierarchyCircularNode<Datum>) => number);
    radius(radius: (node: HierarchyCircularNode<Datum>) => number): this;
    size(): [number, number];
    size(size: [number, number]): this;
    padding(): (node: HierarchyCircularNode<Datum>) => number;
    padding(padding: number): this;
    padding(padding: (node: HierarchyCircularNode<Datum>) => number): this;
}

export function pack<Datum>(): PackLayout<Datum>;



export interface PackCircle {
    r: number;
    x?: number;
    y?: number;
}


export function packSiblings<Datum extends PackCircle>(circles: Array<Datum>): Array<Datum>;

export function packEnclose<Datum extends PackCircle>(circles: Array<Datum>): { r: number, x: number, y: number };

import {Color, RGBColor, ColorSpaceObject, ColorCommonInstance} from '../d3-color';

type ColorSpaceObjectWithHSV = ColorSpaceObject | HSVColor;

export interface HSVColorFactory extends Function {
    (h: number, s: number, v: number, opacity?: number): HSVColor;
    (cssColorSpecifier: string): HSVColor;
    (color: HSVColor | ColorSpaceObject | ColorCommonInstance): HSVColor;
}

export interface HSVColor extends Color {
    h: number;
    s: number;
    v: number;
    opacity: number;
    brighter(k?: number): this;
    darker(k?: number): this;
    rgb(): RGBColor;
}

export var hsv: HSVColorFactory;

import { ColorCommonInstance } from '../d3-color';




export interface ZoomInterpolator extends Function {
    (t: number): ZoomView;
    /**
     * Recommended duration of zoom transition in ms
     */
    duration: number;
}

export interface ColorGammaInterpolationFactory extends Function {
    (a: string | ColorCommonInstance, b: string | ColorCommonInstance): ((t: number) => string);
    gamma(g: number): ColorGammaInterpolationFactory;
}

/**
 * Type zoomView is used to represent a numeric array with three elements.
 * In order of appearance the elements correspond to:
 * - cx: x-coordinate of the center of the viewport
 * - cy: y-coordinate of the center of the viewport
 * - width: size of the viewport
 */
export type ZoomView = [number, number, number];


export function interpolate(a: any, b: null): ((t: number) => null);
export function interpolate(a: number | { valueOf(): number }, b: number): ((t: number) => number);
export function interpolate(a: any, b: ColorCommonInstance): ((t: number) => string);
export function interpolate(a: Date, b: Date): ((t: number) => Date);
export function interpolate(a: string | { toString(): string }, b: string): ((t: number) => string);
export function interpolate<U extends Array<any>>(a: Array<any>, b: U): ((t: number) => U);
export function interpolate(a: number | { valueOf(): number }, b: { valueOf(): number }): ((t: number) => number);
export function interpolate<U extends Object>(a: any, b: U): ((t: number) => U);
export function interpolate(a: any, b: { [key: string]: any }): ((t: number) => { [key: string]: any });


export function interpolateNumber(a: number | { valueOf(): number }, b: number | { valueOf(): number }): ((t: number) => number);

export function interpolateRound(a: number | { valueOf(): number }, b: number | { valueOf(): number }): ((t: number) => number);

export function interpolateString(a: string | { toString(): string }, b: string | { toString(): string }): ((t: number) => string);

export function interpolateDate(a: Date, b: Date): ((t: number) => Date);

export function interpolateArray<A extends Array<any>>(a: Array<any>, b: A): ((t: number) => A);

export function interpolateObject<U extends Object>(a: any, b: U): ((t: number) => U);
export function interpolateObject(a: { [key: string]: any }, b: { [key: string]: any }): ((t: number) => { [key: string]: any });



export function interpolateTransformCss(a: string, b: string): ((t: number) => string);
export function interpolateTransformSvg(a: string, b: string): ((t: number) => string);

/**
 * Create Interpolator for zoom views
 */
export function interpolateZoom(a: ZoomView, b: ZoomView): ZoomInterpolator;


export function quantize<T>(interpolator: ((t: number) => T), n: number): Array<T>;


export var interpolateRgb: ColorGammaInterpolationFactory;

export function interpolateRgbBasis(colors: Array<string | ColorCommonInstance>): ((t: number) => string);
export function interpolateRgbBasisClosed(colors: Array<string | ColorCommonInstance>): ((t: number) => string);

export function interpolateHsl(a: string | ColorCommonInstance, b: string | ColorCommonInstance): ((t: number) => string);
export function interpolateHslLong(a: string | ColorCommonInstance, b: string | ColorCommonInstance): ((t: number) => string);
export function interpolateLab(a: string | ColorCommonInstance, b: string | ColorCommonInstance): ((t: number) => string);
export function interpolateHcl(a: string | ColorCommonInstance, b: string | ColorCommonInstance): ((t: number) => string);
export function interpolateHclLong(a: string | ColorCommonInstance, b: string | ColorCommonInstance): ((t: number) => string);
export var interpolateCubehelix: ColorGammaInterpolationFactory;
export var interpolateCubehelixLong: ColorGammaInterpolationFactory;


export function interpolateBasis(splineNodes: Array<number>): ((t: number) => number);
export function interpolateBasisClosed(splineNodes: Array<number>): ((t: number) => number);

export interface Path {
    moveTo(x: number, y: number): void;
    closePath(): void;
    lineTo(x: number, y: number): void;
    quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void;
    bezierCurveTo(cpx1: number, cpy1: number, cpx2: number, cpy2: number, x: number, y: number): void;
    arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void;
    arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void;
    rect(x: number, y: number, w: number, h: number): void;
    toString(): string;
}

export function path(): Path;

/**
 * Returns the signed area of the specified polygon. If the vertices of the polygon are in counterclockwise order (
 * assuming a coordinate system where the origin ⟨0,0⟩ is in the top-left corner), the returned area is positive;
 * otherwise it is negative, or zero.
 *
 * @param polygon Array of coordinates <x0, y0>, <x1, y1> and so on.
 */
export function polygonArea(polygon: Array<[number, number]>): number;

/**
 * Returns the centroid of the specified polygon.
 *
 * @param polygon Array of coordinates <x0, y0>, <x1, y1> and so on.
 */
export function polygonCentroid(polygon: Array<[number, number]>): [number, number];

/**
 * Returns the convex hull of the specified points using Andrew’s monotone chain algorithm.
 * The returned hull is represented as an array containing a subset of the input points arranged in
 * counterclockwise order. Returns null if points has fewer than three elements.
 *
 * @param points Array of coordinates <x0, y0>, <x1, y1> and so on.
 */
export function polygonHull(points: Array<[number, number]>): Array<[number, number]> | null;

/**
 * Returns true if and only if the specified point is inside the specified polygon.
 *
 * @param polygon Array of coordinates <x0, y0>, <x1, y1> and so on.
 * @param point Coordinates of point <x, y>
 */
export function polygonContains(polygon: Array<[number, number]>, point: [number, number]): boolean;

/**
 * Returns the length of the perimeter of the specified polygon.
 *
 * @param polygon Array of coordinates <x0, y0>, <x1, y1> and so on.
 */
export function polygonLength(polygon: Array<[number, number]>): number;

/**
 * Leaf node of the quadtree.
 */
export interface QuadtreeLeaf<T> {
    data: T;
    next?: QuadtreeLeaf<T>;
}
/**
 * Internal nodes of the quadtree are represented as four-element arrays in left-to-right, top-to-bottom order:
 *
 * 0 - the top-left quadrant, if any.
 * 1 - the top-right quadrant, if any.
 * 2 - the bottom-left quadrant, if any.
 * 3 - the bottom-right quadrant, if any.
 *
 * A child quadrant may be undefined if it is empty.
 */
export interface QuadtreeInternalNode<T> extends Array<QuadtreeInternalNode<T> | QuadtreeLeaf<T> | undefined> { }

export interface Quadtree<T> {
    x(): (d: T) => number;
    x(x: (d: T) => number): this;
    y(): (d: T) => number;
    y(y: (d: T) => number): this;
    extent(): [[number, number], [number, number]] | undefined;
    extent(extend: [[number, number], [number, number]]): this;
    cover(x: number, y: number): this;
    add(datum: T): this;
    addAll(data: Array<T>): this;
    remove(datum: T): this;
    removeAll(data: Array<T>): this;
    copy(): Quadtree<T>;
    root(): QuadtreeInternalNode<T> | QuadtreeLeaf<T>;
    data(): Array<T>;
    size(): number;
    find(x: number, y: number, radius?: number): T | undefined;
    visit(callback: (node: QuadtreeInternalNode<T> | QuadtreeLeaf<T>, x0: number, y0: number, x1: number, y1: number) => (void | boolean)): this;
    visitAfter(callback: (node: QuadtreeInternalNode<T> | QuadtreeLeaf<T>, x0: number, y0: number, x1: number, y1: number) => void): this;
}


export function quadtree(): Quadtree<[number, number]>;
export function quadtree(data: Array<[number, number]>): Quadtree<[number, number]>;
export function quadtree<T>(): Quadtree<T>;
export function quadtree<T>(data: Array<T>, x?: (d: T) => number, y?: (d: T) => number): Quadtree<T>;

/**
 * A d3-queue queue object as returned by queue(...)
 */
export interface Queue {
    /**
     * Adds the specified asynchronous task callback to the queue, with any optional arguments.
     *
     * @param task Task to be executed.The task is a function that will be called when the task should start. It is passed the
     * specified optional arguments and an additional callback as the last argument;
     * the callback must be invoked by the task when it finishes.
     * The task must invoke the callback with two arguments: the error, if any, and the result of the task.
     * To return multiple results from a single callback, wrap the results in an object or array.
     * @param args Additional, optional arguments to be passed into deferred task on invocation
     */
    defer(task: (...args: Array<any>) => void, ...args: any[]): this;
    /**
     * Aborts any active tasks, invoking each active task’s task.abort function, if any.
     * Also prevents any new tasks from starting, and immediately invokes the queue.await or
     * queue.awaitAll callback with an error indicating that the queue was aborted.
     */
    abort(): this;
    /**
     * Sets the callback to be invoked when all deferred tasks have finished (individual result arguments).
     *
     * @param callback Callback function to be executed, when error occured or all deferred tasks
     * have completed. The first argument to the callback is the first error that occurred, or null if no error occurred.
     * If an error occurred, there are no additional arguments to the callback. Otherwise,
     * the callback is passed each result as an additional argument.
     */
    await(callback: (error: any | null, ...results: Array<any>) => void): this;
    /**
     * Sets the callback to be invoked when all deferred tasks have finished (results array).
     *
     * @param callback Callback function to be executed, when error occured or all deferred tasks
     * have completed. The first argument to the callback is the first error that occurred,
     * or null if no error occurred. If an error occurred, there are no additional arguments to the callback.
     * Otherwise, the callback is also passed an array of results as the second argument.
     */
    awaitAll(callback: (error: any | null, results?: Array<any>) => void): this;
}

/**
 * Construct a new queue with the specified concurrency. If concurrency is not specified, the queue has infinite concurrency.
 * Otherwise, concurrency is a positive integer. For example, if concurrency is 1, then all tasks will be run in series.
 * If concurrency is 3, then at most three tasks will be allowed to proceed concurrently; this is useful, for example,
 * when loading resources in a web browser.
 *
 * @param concurrency Maximum number of deferred tasks to execute concurrently.
 */
export function queue(concurrency?: number): Queue;

/**
 * Returns a function for generating random numbers with a uniform distribution).
 * The minimum allowed value of a returned number is min, and the maximum is max.
 * If min is not specified, it defaults to 0; if max is not specified, it defaults to 1.
 */
export function randomUniform(min?: number, max?: number): () => number;

/**
 * Returns a function for generating random numbers with a normal (Gaussian) distribution.
 * The expected value of the generated numbers is mu, with the given standard deviation sigma.
 * If mu is not specified, it defaults to 0; if sigma is not specified, it defaults to 1.
 */
export function randomNormal(mu?: number, sigma?: number): () => number;

/**
 * Returns a function for generating random numbers with a log-normal distribution. The expected value of the random variable’s natural logrithm is mu,
 * with the given standard deviation sigma. If mu is not specified, it defaults to 0; if sigma is not specified, it defaults to 1.
 */
export function randomLogNormal(mu?: number, sigma?: number): () => number;

/**
 * Returns a function for generating random numbers with a Bates distribution with n independent variables.
 */
export function randomBates(n: number): () => number;

/**
 * Returns a function for generating random numbers with an Irwin–Hall distribution with n independent variables.
 */
export function randomIrwinHall(n: number): () => number;

/**
 * Returns a function for generating random numbers with an exponential distribution with the rate lambda;
 * equivalent to time between events in a Poisson process with a mean of 1 / lambda.
 */
export function randomExponential(lambda: number): () => number;

import { DSVParsedArray, DSVRowString, DSVRowAny } from '../d3-dsv';

export interface Request {
    abort(): this;

    get(): this;
    get<RequestData>(data: RequestData): this;
    get<ResponseData>(callback: (error: any, d: ResponseData) => void): this;
    get<RequestData, ResponseData>(data: RequestData, callback: (error: any, d: ResponseData) => void): this;

    header(name: string): string;
    header(name: string, value: string | null): this;

    mimeType(): string | null;
    mimeType(value: string | null): this;

    on(type: 'beforesend'): (this: this, xhr: XMLHttpRequest) => void;
    on(type: 'progress'): (this: this, progressEvent: ProgressEvent) => void;
    on(type: 'error'): (this: this, error: any) => void;
    on<ResponseData>(type: 'load'): (this: this, data: ResponseData) => void;
    on(type: string): (this: this, data: any) => void;
    on(type: string, listener: null): this;
    on(type: 'beforesend', listener: (this: this, xhr: XMLHttpRequest) => void): this;
    on(type: 'progress', listener: (this: this, progressEvent: ProgressEvent) => void): this;
    on(type: 'error', listener: (this: this, error: any) => void): this;
    on<ResponseData>(type: 'load', listener: (this: this, data: ResponseData) => void): this;
    on(type: string, listener: (this: this, data: any) => void): this;

    password(): string | null;
    password(value: string): this;

    post(): this;
    post<RequestData>(data: RequestData): this;
    post<ResponseData>(callback: (this: this, error: any, d: ResponseData) => void): this;
    post<RequestData, ResponseData>(data: RequestData, callback: (this: this, error: any, d: ResponseData) => void): this;

    response<ResponseData>(callback: (this: this, response: XMLHttpRequest) => ResponseData): this;

    responseType(): string | null;
    responseType(value: string): this;

    send(method: string): this;
    send<RequestData>(method: string, data: RequestData): this;
    send<ResponseData>(method: string, callback: (this: this, error: any | null, d: ResponseData | null) => void): this;
    send<RequestData, ResponseData>(method: string, data: RequestData, callback: (this: this, error: any | null, d: ResponseData | null) => void): this;

    timeout(): number;
    timeout(value: number): this;

    user(): string | null;
    user(value: string): this;
}

export interface DsvRequest extends Request {
    row<ParsedRow extends DSVRowAny>(value: (rawRow: DSVRowString, index: number, columns: Array<string>) => ParsedRow): DsvRequest;
}

export function csv(url: string): DsvRequest;
export function csv(url: string, callback: (this: DsvRequest, error: any, d: DSVParsedArray<DSVRowString>) => void): DsvRequest;
export function csv<ParsedRow extends DSVRowAny>(url: string, row: (rawRow: DSVRowString, index: number, columns: Array<string>) => ParsedRow, callback: (this: DsvRequest, error: any, d: DSVParsedArray<ParsedRow>) => void): DsvRequest;

export function html(url: string): Request;
export function html(url: string, callback: (this: Request, error: any, d: DocumentFragment) => void): Request;

export function json(url: string): Request;
export function json<ParsedObject extends { [key: string]: any }>(url: string, callback: (this: Request, error: any, d: ParsedObject) => void): Request;

export function request(url: string): Request;
export function request(url: string, callback: (this: Request, error: any, d: XMLHttpRequest) => void): Request;

export function text(url: string): Request;
export function text(url: string, callback: (this: Request, error: any, d: string) => void): Request;

export function tsv(url: string): DsvRequest;
export function tsv(url: string, callback: (this: DsvRequest, error: any, d: DSVParsedArray<DSVRowString>) => void): DsvRequest;
export function tsv<ParsedRow extends DSVRowAny>(url: string, row: (rawRow: DSVRowString, index: number, columns: Array<string>) => ParsedRow, callback: (this: DsvRequest, error: any, d: DSVParsedArray<ParsedRow>) => void): DsvRequest;

export function xml(url: string): Request;
export function xml(url: string, callback: (this: Request, error: any, d: any) => void): Request;

import { CountableTimeInterval, TimeInterval } from '../d3-time';



export interface InterpolatorFactory<T, U> {
    (a: T, b: T): ((t: number) => U);
}




export interface ScaleLinear<Range, Output> {
    (value: number | { valueOf(): number }): Output;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invert(value: number | { valueOf(): number }): number;
    domain(): Array<number>;
    domain(domain: Array<number | { valueOf(): number }>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric.
     */
    rangeRound(range: Array<number | { valueOf(): number }>): this;
    clamp(): boolean;
    clamp(clamp: boolean): ScaleLinear<Range, Output>;
    interpolate(): InterpolatorFactory<any, any>;
    interpolate(interpolate: InterpolatorFactory<Range, Output>): this;
    interpolate<NewOutput>(interpolate: InterpolatorFactory<Range, NewOutput>): ScaleLinear<Range, NewOutput>;
    ticks(count?: number): Array<number>;
    tickFormat(count?: number, specifier?: string): ((d: number | { valueOf(): number }) => string);
    nice(count?: number): this;
    copy(): ScaleLinear<Range, Output>;
}

export function scaleLinear(): ScaleLinear<number, number>;
export function scaleLinear<Output>(): ScaleLinear<Output, Output>;
export function scaleLinear<Range, Output>(): ScaleLinear<Range, Output>;



export interface ScalePower<Range, Output> {
    (value: number | { valueOf(): number }): Output;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invert(value: number | { valueOf(): number }): number;
    domain(): Array<number>;
    domain(domain: Array<number | { valueOf(): number }>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric.
     */
    rangeRound(range: Array<number | { valueOf(): number }>): this;
    clamp(): boolean;
    clamp(clamp: boolean): this;
    interpolate(): InterpolatorFactory<any, any>;
    interpolate(interpolate: InterpolatorFactory<Range, Output>): this;
    interpolate<NewOutput>(interpolate: InterpolatorFactory<Range, NewOutput>): ScalePower<Range, NewOutput>;
    ticks(count?: number): Array<number>;
    tickFormat(count?: number, specifier?: string): ((d: number | { valueOf(): number }) => string);
    nice(count?: number): this;
    copy(): ScalePower<Range, Output>;

    exponent(): number;
    exponent(exponent: number): this;
}

export function scalePow(): ScalePower<number, number>;
export function scalePow<Output>(): ScalePower<Output, Output>;
export function scalePow<Range, Output>(): ScalePower<Range, Output>;

export function scaleSqrt(): ScalePower<number, number>;
export function scaleSqrt<Output>(): ScalePower<Output, Output>;
export function scaleSqrt<Range, Output>(): ScalePower<Range, Output>;



export interface ScaleLogarithmic<Range, Output> {
    (value: number | { valueOf(): number }): Output;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invert(value: number | { valueOf(): number }): number;
    domain(): Array<number>;
    domain(domain: Array<number | { valueOf(): number }>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric.
     */
    rangeRound(range: Array<number | { valueOf(): number }>): this;
    clamp(): boolean;
    clamp(clamp: boolean): this;
    interpolate(): InterpolatorFactory<any, any>;
    interpolate(interpolate: InterpolatorFactory<Range, Output>): this;
    interpolate<NewOutput>(interpolate: InterpolatorFactory<Range, NewOutput>): ScaleLogarithmic<Range, NewOutput>;
    ticks(count?: number): Array<number>;
    tickFormat(count?: number, specifier?: string): ((d: number | { valueOf(): number }) => string);
    nice(count?: number): this;
    copy(): ScaleLogarithmic<Range, Output>;

    base(): number;
    base(base: number): this;
}

export function scaleLog(): ScaleLogarithmic<number, number>;
export function scaleLog<Output>(): ScaleLogarithmic<Output, Output>;
export function scaleLog<Range, Output>(): ScaleLogarithmic<Range, Output>;



export interface ScaleIdentity {
    (value: number | { valueOf(): number }): number;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invert(value: number | { valueOf(): number }): number;
    domain(): Array<number>;
    domain(domain: Array<number | { valueOf(): number }>): this;
    range(): Array<number>;
    range(range: Array<Range | { valueOf(): number }>): this;
    ticks(count?: number): Array<number>;
    tickFormat(count?: number, specifier?: string): ((d: number | { valueOf(): number }) => string);
    nice(count?: number): this;
    copy(): ScaleIdentity;
}

export function scaleIdentity(): ScaleIdentity;



export interface ScaleTime<Range, Output> {
    (value: Date): Output;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invert(value: number | { valueOf(): number }): Date;
    domain(): Array<Date>;
    domain(domain: Array<Date>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric.
     */
    rangeRound(range: Array<number | { valueOf(): number }>): this;
    clamp(): boolean;
    clamp(clamp: boolean): this;
    interpolate(): InterpolatorFactory<any, any>;
    interpolate(interpolate: InterpolatorFactory<Range, Output>): this;
    interpolate<NewOutput>(interpolate: InterpolatorFactory<Range, NewOutput>): ScaleTime<Range, NewOutput>;
    ticks(): Array<Date>;
    ticks(count: number): Array<Date>;
    ticks(interval: TimeInterval): Array<Date>;
    tickFormat(): ((d: Date) => string);
    tickFormat(count: number, specifier?: string): ((d: Date) => string);
    tickFormat(interval: TimeInterval, specifier?: string): ((d: Date) => string);
    nice(): this;
    nice(count: number): this;
    nice(interval: CountableTimeInterval, step?: number): this;
    copy(): ScaleTime<Range, Output>;
}

export function scaleTime(): ScaleTime<number, number>;
export function scaleTime<Output>(): ScaleTime<Output, Output>;
export function scaleTime<Range, Output>(): ScaleTime<Range, Output>;

export function scaleUtc(): ScaleTime<number, number>;
export function scaleUtc<Output>(): ScaleTime<Output, Output>;
export function scaleUtc<Range, Output>(): ScaleTime<Range, Output>;



export interface ScaleSequential<Output> {
    (value: number | { valueOf(): number }): Output;
    domain(): [number, number];
    domain(domain: [number | { valueOf(): number }, number | { valueOf(): number }]): this;
    clamp(): boolean;
    clamp(clamp: boolean): this;
    interpolator(): ((t: number) => Output);
    interpolator(interpolator: ((t: number) => Output)): this;
    interpolator<NewOutput>(interpolator: ((t: number) => NewOutput)): ScaleSequential<NewOutput>;
    copy(): ScaleSequential<Output>;
}

export function scaleSequential<Output>(interpolator: ((t: number) => Output)): ScaleSequential<Output>;



export function interpolateViridis(t: number): string;

export function interpolateMagma(t: number): string;

export function interpolateInferno(t: number): string;

export function interpolatePlasma(t: number): string;

export function interpolateRainbow(t: number): string;

export function interpolateWarm(t: number): string;

export function interpolateCool(t: number): string;

export function interpolateCubehelixDefault(t: number): string;


export interface ScaleQuantize<Range> {
    (value: number | { valueOf(): number }): Range;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invertExtent(value: Range): [number, number];
    domain(): [number, number];
    domain(domain: [number | { valueOf(): number }, number | { valueOf(): number }]): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    ticks(count?: number): Array<number>;
    tickFormat(count?: number, specifier?: string): ((d: number | { valueOf(): number }) => string);
    nice(count?: number): this;
    copy(): ScaleQuantize<Range>;
}

export function scaleQuantize(): ScaleQuantize<number>;
export function scaleQuantize<Range>(): ScaleQuantize<Range>;


export interface ScaleQuantile<Range> {
    (value: number | { valueOf(): number }): Range;
    invertExtent(value: Range): [number, number];
    domain(): Array<number>;
    domain(domain: Array<number | { valueOf(): number }>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    quantiles(): Array<number>;
    copy(): ScaleQuantile<Range>;
}

export function scaleQuantile(): ScaleQuantile<number>;
export function scaleQuantile<Range>(): ScaleQuantile<Range>;


export interface ScaleThreshold<Domain extends number | string | Date, Range> {
    (value: Domain): Range;
    /**
     * Important: While value should come out of range R, this is method is only applicable to
     * values that can be coerced to numeric. Otherwise, returns NaN
     */
    invertExtent(value: Range): [Domain, Domain] | [undefined, Domain] | [Domain, undefined] | [undefined, undefined];
    domain(): Array<Domain>;
    domain(domain: Array<Domain>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    copy(): ScaleThreshold<Domain, Range>;
}

export function scaleThreshold(): ScaleThreshold<number, number>;
export function scaleThreshold<Domain extends number | string | Date, Range>(): ScaleThreshold<Domain, Range>;



export interface ScaleOrdinal<Domain extends { toString(): string }, Range> {
    (x: Domain): Range;
    domain(): Array<Domain>;
    domain(domain: Array<Domain>): this;
    range(): Array<Range>;
    range(range: Array<Range>): this;
    unknown(): Range | { name: 'implicit' };
    unknown(value: Range | { name: 'implicit' }): this;
    copy(): ScaleOrdinal<Domain, Range>;
}

export function scaleOrdinal<Range>(range?: Array<Range>): ScaleOrdinal<string, Range>;
export function scaleOrdinal<Domain extends { toString(): string }, Range>(range?: Array<Range>): ScaleOrdinal<Domain, Range>;

export const scaleImplicit: { name: 'implicit' };



export interface ScaleBand<Domain extends { toString(): string }> {
    (x: Domain): number | undefined;
    domain(): Array<Domain>;
    domain(domain: Array<Domain>): this;
    range(): [number, number];
    range(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this;
    rangeRound(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this;
    round(): boolean;
    round(round: boolean): this;
    paddingInner(): number;
    paddingInner(padding: number): this;
    paddingOuter(): number;
    paddingOuter(padding: number): this;
    /**
     * Returns the inner padding.
     */
    padding(): number;
    /**
     * A convenience method for setting the inner and outer padding to the same padding value.
     */
    padding(padding: number): this;
    align(): number;
    align(align: number): this;
    bandwidth(): number;
    step(): number;
    copy(): ScaleBand<Domain>;
}

export function scaleBand(): ScaleBand<string>;
export function scaleBand<Domain extends { toString(): string }>(): ScaleBand<Domain>;


export interface ScalePoint<Domain extends { toString(): string }> {
    (x: Domain): number | undefined;
    domain(): Array<Domain>;
    domain(domain: Array<Domain>): this;
    range(): [number, number];
    range(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this;
    rangeRound(range: [number | { valueOf(): number }, number | { valueOf(): number }]): this;
    round(): boolean;
    round(round: boolean): this;
    /**
     * Returns the current outer padding which defaults to 0.
     * The outer padding determines the ratio of the range that is reserved for blank space
     * before the first point and after the last point.
     */
    padding(): number;
    /**
     * Sets the outer padding to the specified value which must be in the range [0, 1].
     * The outer padding determines the ratio of the range that is reserved for blank space
     * before the first point and after the last point.
     */
    padding(padding: number): this;
    align(): number;
    align(align: number): this;
    bandwidth(): number;
    step(): number;
    copy(): ScalePoint<Domain>;
}

export function scalePoint(): ScalePoint<string>;
export function scalePoint<Domain extends { toString(): string }>(): ScalePoint<Domain>;



export const schemeCategory10: Array<string>;

export const schemeCategory20: Array<string>;

export const schemeCategory20b: Array<string>;

export const schemeCategory20c: Array<string>;

/**An array of eight categorical colors represented as RGB hexadecimal strings. */
export const schemeAccent: Array<string>;
/**An array of eight categorical colors represented as RGB hexadecimal strings. */
export const schemeDark2: Array<string>;
/**An array of twelve categorical colors represented as RGB hexadecimal strings. */
export const schemePaired: Array<string>;
/**An array of nine categorical colors represented as RGB hexadecimal strings. */
export const schemePastel1: Array<string>;
/**An array of eight categorical colors represented as RGB hexadecimal strings. */
export const schemePastel2: Array<string>;
/**An array of nine categorical colors represented as RGB hexadecimal strings. */
export const schemeSet1: Array<string>;
/**An array of eight categorical colors represented as RGB hexadecimal strings. */
export const schemeSet2: Array<string>;
/**An array of twelve categorical colors represented as RGB hexadecimal strings. */
export const schemeSet3: Array<string>;

/**Given a number value in the range [0,1], returns the corresponding color from the “BrBG” diverging color scheme represented as an RGB string. */
export function interpolateBrBG(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “PRGn” diverging color scheme represented as an RGB string.*/
export function interpolatePRGn(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “PiYG” diverging color scheme represented as an RGB string.*/
export function interpolatePiYG(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “PuOr” diverging color scheme represented as an RGB string.*/
export function interpolatePuOr(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “RdBu” diverging color scheme represented as an RGB string.*/
export function interpolateRdBu(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “RdGy” diverging color scheme represented as an RGB string.*/
export function interpolateRdGy(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “RdYlBu” diverging color scheme represented as an RGB string.*/
export function interpolateRdYlBu(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “RdYlGn” diverging color scheme represented as an RGB string.*/
export function interpolateRdYlGn(value: number): string;
/** Given a number t in the range [0,1], returns the corresponding color from the “Spectral” diverging color scheme represented as an RGB string.*/
export function interpolateSpectral(value: number): string;

/**Given a number t in the range [0,1], returns the corresponding color from the “Blues” sequential color scheme represented as an RGB string. */
export function interpolateBlues(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “Greens” sequential color scheme represented as an RGB string. */
export function interpolateGreens(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “Greys” sequential color scheme represented as an RGB string. */
export function interpolateGreys(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “Oranges” sequential color scheme represented as an RGB string. */
export function interpolateOranges(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “Purples” sequential color scheme represented as an RGB string. */
export function interpolatePurples(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “Reds” sequential color scheme represented as an RGB string. */
export function interpolateReds(value: number): string;


/**Given a number t in the range [0,1], returns the corresponding color from the “BuGn” sequential color scheme represented as an RGB string. */
export function interpolateBuGn(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “BuPu” sequential color scheme represented as an RGB string. */
export function interpolateBuPu(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “GnBu” sequential color scheme represented as an RGB string. */
export function interpolateGnBu(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “OrRd” sequential color scheme represented as an RGB string. */
export function interpolateOrRd(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “PuBuGn” sequential color scheme represented as an RGB string. */
export function interpolatePuBuGn(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “PuBu” sequential color scheme represented as an RGB string. */
export function interpolatePuBu(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “PuRd” sequential color scheme represented as an RGB string. */
export function interpolatePuRd(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “RdPu” sequential color scheme represented as an RGB string. */
export function interpolateRdPu(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “YlGnBu” sequential color scheme represented as an RGB string. */
export function interpolateYlGnBu(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “YlGn” sequential color scheme represented as an RGB string. */
export function interpolateYlGn(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “YlOrBr” sequential color scheme represented as an RGB string. */
export function interpolateYlOrBr(value: number): string;
/**Given a number t in the range [0,1], returns the corresponding color from the “YlOrRd” sequential color scheme represented as an RGB string. */
export function interpolateYlOrRd(value: number): string;


/**
 * BaseType serves as an alias for the 'minimal' data type which can be selected
 * without 'd3-selection' trying to use properties internally which would otherwise not
 * be supported.
 */
export type BaseType = Element | EnterElement | Window;

export interface ArrayLike<T> {
    length: number;
    item(index: number): T;
    [index: number]: T;
}


export interface EnterElement {
    ownerDocument: Document;
    namespaceURI: string;
    appendChild(newChild: Node): Node;
    insertBefore(newChild: Node, refChild: Node): Node;
    querySelector(selectors: string): Element;
    querySelectorAll(selectors: string): NodeListOf<Element>;
}

/**
 * Container element type usable for mouse/touch functions
 */
export type ContainerElement = HTMLElement | SVGSVGElement | SVGGElement;


/**
 * Type for optional parameters map, when dispatching custom events
 * on a selection
 */
export type CustomEventParameters = {
    /**
     * If true, the event is dispatched to ancestors in reverse tree order
     */
    bubbles: boolean;
    /**
     * If true, event.preventDefault is allowed
     */
    cancelable: boolean;
    /**
     * Any custom data associated with the event
     */
    detail: any;
}

/**
 * Callback type for selections and transitions
 */
export type ValueFn<Element, Datum, Result> = (this: Element, datum: Datum, index: number, groups: Array<Element> | ArrayLike<Element>) => Result;


/**
 * TransitionLike is a helper interface to represent a quasi-Transition, without specifying the full Transition  interface in this file.
 * For example, whereever d3-zoom allows a Transition to be passed in as an argument, it internally immediately invokes its `selection()`
 * method to retrieve the underlying Selection object before proceeding.
 * d3-brush uses a subset of Transition methods internally.
 * The use of this interface instead of the full imported Transition interface is [referred] to achieve
 * two things:
 * (1) the d3-transition module may not be required by a projects use case,
 * (2) it avoid avoids possible complications from 'module augmentation' from d3-transition to Selection.
 */
export interface TransitionLike<GElement extends BaseType, Datum> {
    selection(): Selection<GElement, Datum, any, any>;
    on(type: string, listener: null): TransitionLike<GElement, Datum>;
    on(type: string, listener: ValueFn<GElement, Datum, void>): TransitionLike<GElement, Datum>;
    tween(name: string, tweenFn: null): TransitionLike<GElement, Datum>;
    tween(name: string, tweenFn: ValueFn<GElement, Datum, ((t: number) => void)>): TransitionLike<GElement, Datum>;
}




export function select<GElement extends BaseType, OldDatum>(selector: string): Selection<GElement, OldDatum, HTMLElement, any>;
export function select<GElement extends BaseType, OldDatum>(node: GElement): Selection<GElement, OldDatum, null, undefined>;

export function selectAll(): Selection<null, undefined, null, undefined>; // _groups are set to empty array, first generic type is set to null by convention
export function selectAll(selector: null): Selection<null, undefined, null, undefined>; // _groups are set to empty array, first generic type is set to null by convention
export function selectAll<GElement extends BaseType, OldDatum>(selector: string): Selection<GElement, OldDatum, HTMLElement, any>;
export function selectAll<GElement extends BaseType, OldDatum>(nodes: GElement[]): Selection<GElement, OldDatum, null, undefined>;
export function selectAll<GElement extends BaseType, OldDatum>(nodes: ArrayLike<GElement>): Selection<GElement, OldDatum, null, undefined>;



interface Selection<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {

    // Sub-selection -------------------------

    select<DescElement extends BaseType>(selector: string): Selection<DescElement, Datum, PElement, PDatum>;
    select<DescElement extends BaseType>(selector: null): Selection<null, undefined, PElement, PDatum>; // _groups are set to empty array, first generic type is set to null by convention
    select<DescElement extends BaseType>(selector: ValueFn<GElement, Datum, DescElement>): Selection<DescElement, Datum, PElement, PDatum>;

    selectAll(): Selection<null, undefined, GElement, Datum>; // _groups are set to empty array, first generic type is set to null by convention
    selectAll(selector: null): Selection<null, undefined, GElement, Datum>; // _groups are set to empty array, first generic type is set to null by convention
    selectAll<DescElement extends BaseType, OldDatum>(selector: string): Selection<DescElement, OldDatum, GElement, Datum>;
    selectAll<DescElement extends BaseType, OldDatum>(selector: ValueFn<GElement, Datum, Array<DescElement> | ArrayLike<DescElement>>): Selection<DescElement, OldDatum, GElement, Datum>;

    // Modifying -------------------------------

    attr(name: string): string;
    attr(name: string, value: null): this;
    attr(name: string, value: string | number | boolean): this;
    attr(name: string, value: ValueFn<GElement, Datum, string | number | boolean>): this;

    classed(name: string): boolean;
    classed(name: string, value: boolean): this;
    classed(name: string, value: ValueFn<GElement, Datum, boolean>): this;

    style(name: string): string;
    style(name: string, value: null): this;
    style(name: string, value: string | number | boolean, priority?: null | 'important'): this;
    style(name: string, value: ValueFn<GElement, Datum, string | number | boolean>, priority?: null | 'important'): this;

    property(name: string): any;
    /**
     * Look up a local variable on the first node of this selection. Note that this is not equivalent to `local.get(selection.node())` in that it will not look up locals set on the parent node(s).
     *
     * @param name The `d3.local` variable to look up.
     */
    property<T>(name: Local<T>): T | undefined;
    property(name: string, value: ValueFn<GElement, Datum, any>): this;
    property(name: string, value: null): this;
    property(name: string, value: any): this;
    /**
     * Store a value in a `d3.local` variable. This is equivalent to `selection.each(function (d, i, g) { name.set(this, value.call(this, d, i, g)); })` but more concise.
     *
     * @param name A `d3.local` variable
     * @param value A callback that returns the value to store
     */
    property<T>(name: Local<T>, value: ValueFn<GElement, Datum, T>): this;
    /**
     * Store a value in a `d3.local` variable for each node in the selection. This is equivalent to `selection.each(function () { name.set(this, value); })` but more concise.
     *
     * @param name A `d3.local` variable
     * @param value A callback that returns the value to store
     */
    property<T>(name: Local<T>, value: T): this;

    text(): string;
    text(value: string | number | boolean): this;
    text(value: ValueFn<GElement, Datum, string | number | boolean>): this;

    html(): string;
    html(value: string): this;
    html(value: ValueFn<GElement, Datum, string>): this;

    append<ChildElement extends BaseType>(type: string): Selection<ChildElement, Datum, PElement, PDatum>;
    append<ChildElement extends BaseType>(type: ValueFn<GElement, Datum, ChildElement>): Selection<ChildElement, Datum, PElement, PDatum>;

    insert<ChildElement extends BaseType>(type: string, before: string): Selection<ChildElement, Datum, PElement, PDatum>;
    insert<ChildElement extends BaseType>(type: ValueFn<GElement, Datum, ChildElement>, before: string): Selection<ChildElement, Datum, PElement, PDatum>;
    insert<ChildElement extends BaseType>(type: string, before: ValueFn<GElement, Datum, BaseType>): Selection<ChildElement, Datum, PElement, PDatum>;
    insert<ChildElement extends BaseType>(type: ValueFn<GElement, Datum, ChildElement>, before: ValueFn<GElement, Datum, BaseType>): Selection<ChildElement, Datum, PElement, PDatum>;

    /**
     * Removes the selected elements from the document.
     * Returns this selection (the removed elements) which are now detached from the DOM.
     */
    remove(): this;

    merge(other: Selection<GElement, Datum, PElement, PDatum>): Selection<GElement, Datum, PElement, PDatum>;

    filter(selector: string): this;
    filter(selector: ValueFn<GElement, Datum, boolean>): this;



    sort(comparator?: (a: Datum, b: Datum) => number): this;

    order(): this;

    raise(): this;

    lower(): this;


    // Data Join ---------------------------------

    datum(): Datum;
    datum(value: null): Selection<GElement, undefined, PElement, PDatum>;
    datum<NewDatum>(value: ValueFn<GElement, Datum, NewDatum>): Selection<GElement, NewDatum, PElement, PDatum>;
    datum<NewDatum>(value: NewDatum): Selection<GElement, NewDatum, PElement, PDatum>;

    data(): Datum[];
    data<NewDatum>(data: Array<NewDatum>, key?: ValueFn<GElement | PElement, Datum | NewDatum, string>): Selection<GElement, NewDatum, PElement, PDatum>;
    data<NewDatum>(data: ValueFn<PElement, PDatum, Array<NewDatum>>, key?: ValueFn<GElement | PElement, Datum | NewDatum, string>): Selection<GElement, NewDatum, PElement, PDatum>;

    enter(): Selection<EnterElement, Datum, PElement, PDatum>;

    // The type Datum on the exit items is actually of the type prior to calling data(...), as by definition, no new data of type NewDatum exists for these
    // elements. Due to the chaining, .data(...).exit(...), however, the definition would imply that the exit group elements have assumed the NewDatum type.
    // This seems to imply the following workaroud: Recast the exit Selection to OldDatum, if needed, or ommit and allow exit group elements to be of type any.
    exit<OldDatum>(): Selection<GElement, OldDatum, PElement, PDatum>;

    // Event Handling -------------------

    on(type: string): ValueFn<GElement, Datum, void>;
    on(type: string, listener: null): this;
    on(type: string, listener: ValueFn<GElement, Datum, void>, capture?: boolean): this;


    dispatch(type: string, parameters?: CustomEventParameters): this;
    dispatch(type: string, parameters?: ValueFn<GElement, Datum, CustomEventParameters>): this;

    // Control Flow ----------------------

    each(valueFn: ValueFn<GElement, Datum, void>): this;

    call(func: (selection: Selection<GElement, Datum, PElement, PDatum>, ...args: any[]) => void, ...args: any[]): this;

    empty(): boolean;

    node(): GElement;
    nodes(): Array<GElement>;

    size(): number;


}


interface SelectionFn extends Function {
    (): Selection<HTMLElement, any, null, undefined>;
}
export var selection: SelectionFn;



interface BaseEvent {
    type: string;
    sourceEvent?: any; // Could be of all sorts of types, too general: BaseEvent | Event | MouseEvent | TouchEvent | ... | OwnCustomEventType;
}

export var event: any; // Could be of all sorts of types, too general: BaseEvent | Event | MouseEvent | TouchEvent | ... | OwnCustomEventType;


export function customEvent<Context, Result>(event: BaseEvent, listener: (this: Context, ...args: any[]) => Result, that: Context, ...args: any[]): Result;


/**
 * Get (x, y)-coordinates of the current event relative to the specified container element.
 * The coordinates are returned as a two-element array of numbers [x, y].
 * @param container
 */
export function mouse(container: ContainerElement): [number, number];


export function touch(container: ContainerElement, identifier: number): [number, number];
export function touch(container: ContainerElement, touches: TouchList, identifier: number): [number, number];

export function touches(container: ContainerElement, touches?: TouchList): Array<[number, number]>;



export interface Local<T> {
    /**
     * Retrieves a local variable stored on the node (or one of its parents).
     */
    get(node: Element): T | undefined;
    /**
     * Deletes the value associated with the given node. Values stored on ancestors are not affected, meaning that child nodes will still see inherited values.
     *
     * This function returns true if there was a value stored directly on the node, and false otherwise.
     */
    remove(node: Element): boolean;
    /**
     * Store a value for this local variable. Calling `.get()` on children of this node will also retrieve the variable's value.
     */
    set(node: Element, value: T): Element;
    /**
     * Obtain a string with the internally assigned property name for the local
     * which is used to store the value on a node
     */
    toString(): string;
}

/**
 * Obtain a new local variable
 */
export function local<T>(): Local<T>;


/**
 * Type for object literal containing local name with related fully qualified namespace
 */
export type NamespaceLocalObject = {
    /**
     * Fully qualified namespace
     */
    space: string,
    /**
     * Name of the local to be namespaced.
     */
    local: string
}

/**
 * Obtain an object with properties of fully qualified namespace string and
 * name of local by parsing a shorthand string "prefix:local". If the prefix
 * does not exist in the "namespaces" object provided by d3-selection, then
 * the local name is returned as a simple string.
 *
 * @param prefixedLocal A string composed of the namespace prefix and local
 * name separated by colon, e.g. "svg:text".
 */
export function namespace(prefixedLocal: string): NamespaceLocalObject | string;



/**
 * Type for maps of namespace prefixes to corresponding fully qualified namespace strings
 */
export type NamespaceMap = { [prefix: string]: string };

/**
 * Map of namespace prefixes to corresponding fully qualified namespace strings
 */
export var namespaces: NamespaceMap;



export function window(DOMNode: Window | Document | Element): Window;




/**
 * Returns a closure structure which can be invoked in the 'this' context
 * of a group element. Depending on the use of namespacing, the NewGElement can be HTMLElement,
 * SVGElement an extension thereof or an element from a different namespace.
 *
 * @param elementName Name of the element to be added
 */
export function creator<NewGElement extends Element>(elementName: string): (this: BaseType) => NewGElement;

/**
 * Returns a closure structure which can be invoked in the 'this' context
 * of a group element. Returns true, if the element in the 'this' context matches the selector
 *
 * @param selector A valid selector string
 */
export function matcher<GElement extends Element>(selector: string): (this: BaseType) => boolean;


export function selector<DescElement extends Element>(selector: string): (this: BaseType) => DescElement

export function selectorAll<DescElement extends Element>(selector: string): (this: BaseType) => NodeListOf<DescElement>;

import {Selection, BaseType, ArrayLike, ValueFn} from '../d3-selection';
import {Transition} from '../d3-transition';


export type ValueMap<Element, Datum> = { [key: string]: number | string | boolean | null | ValueFn<Element, Datum, number | string | boolean | null> };

declare module '../d3-selection' {
    export interface Selection<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {
        /**
         * Set multiple attributes on the given selection. Attribute values may be constant or derived from each node and its bound data.
         *
         * @param attrs An object used as a map of attribute names to set
         */
        attrs(attrs: ValueMap<GElement, Datum>): this;

        /**
         * Derive a map of attributes to be set on the selection.
         *
         * @param attrs A function that returns an object of attribute names and values to set.
         */
        attrs(attrs: ValueFn<GElement, Datum, ValueMap<GElement, Datum>>): this;

        /**
         * Set multiple CSS style properties on the given selection. Style properties may be constant or derived from each node and its bound data.
         *
         * @param style An object used as a map of style properties to set.
         * @param priority The CSS priority (either "important" or undefined).
         */
        styles(style: ValueMap<GElement, Datum>, priority?: 'important'): this;

        /**
         * Derive a map of style properties to be set on the selection.
         *
         * @param style A function that returns an object of style properties and the values to be set.
         * @param priority The CSS priority (either "important" or undefined)
         */
        styles(style: ValueFn<GElement, Datum, ValueMap<GElement, Datum>>, priority?: 'important'): this;

        /**
         * Set multiple object properties directly on the selection's node(s). Property values may be constants or derived from each node and its bound data.
         *
         * @param props An object used as a map of object properties to be set.
         */
        properties(props: ValueMap<GElement, Datum>): this;

        /**
         * Derive a map of object properties to be set on the selection's node(s).
         *
         * @param props A function that returns an object of properties and their values.
         */
        properties(props: ValueFn<GElement, Datum, ValueMap<GElement, Datum>>): this;
    }
}

declare module '../d3-transition' {
    export interface Transition<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {
        /**
         * Set multiple attribute values. The transition will animate from the present value to the new value. Attribute values may be constant or derived from each node and its bound data.
         *
         * @param attrs An object used as a map of attributes and their values.
         */
        attrs(attrs: ValueMap<GElement, Datum>): this;

        /**
         * Derive a map of attribute values to set.
         *
         * @param attrs A function returning a map of attributes and their values.
         */
        attrs(attrs: ValueFn<GElement, Datum, ValueMap<GElement, Datum>>): this;

        /**
         * Set multiple style properties. The transition will animate from the present value to the new value. Attribute values may be constant or derived from each node and its bound data.
         *
         * @param style A map of style properties and their values
         * @param priority The CSS priority (either "important" or undefined)
         */
        styles(style: ValueMap<GElement, Datum>, priority?: 'important'): this;

        /**
         * Derive a map of style properties to be set.
         *
         * @param style A function returning a map of style properties and their values
         * @param priority The CSS priority (either "important" or undefined)
         */
        styles(style: ValueFn<GElement, Datum, ValueMap<GElement, Datum>>, priority?: 'important'): this;
    }
}


export interface DefaultArcObject {
    innerRadius: number;
    outerRadius: number;
    startAngle: number;
    endAngle: number;
    padAngle: number;
}

export interface Arc<This, Datum> {
    (this: This, d: Datum, ...args: any[]): string | undefined;
    centroid(d: Datum, ...args: any[]): [number, number];
    innerRadius(): (this: This, d: Datum, ...args: any[]) => number;
    innerRadius(radius: number): this;
    innerRadius(radius: (this: This, d: Datum, ...args: any[]) => number): this;
    outerRadius(): (this: This, d: Datum, ...args: any[]) => number;
    outerRadius(radius: number): this;
    outerRadius(radius: (this: This, d: Datum, ...args: any[]) => number): this;
    cornerRadius(): (this: This, d: Datum, ...args: any[]) => number;
    cornerRadius(radius: number): this;
    cornerRadius(radius: (this: This, d: Datum, ...args: any[]) => number): this;
    startAngle(): (this: This, d: Datum, ...args: any[]) => number;
    startAngle(angle: number): this;
    startAngle(angle: (this: This, d: Datum, ...args: any[]) => number): this;
    endAngle(): (this: This, d: Datum, ...args: any[]) => number;
    endAngle(angle: number): this;
    endAngle(angle: (this: This, d: Datum, ...args: any[]) => number): this;
    padAngle(): (this: This, d: Datum, ...args: any[]) => number;
    padAngle(angle: number): this;
    padAngle(angle: (this: This, d: Datum, ...args: any[]) => number): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;
}

export function arc(): Arc<any, DefaultArcObject>;
export function arc<Datum>(): Arc<any, Datum>;
export function arc<This, Datum>(): Arc<This, Datum>;



export interface PieArcDatum<T> {
    data: T;
    value: number;
    index: number;
    startAngle: number;
    endAngle: number;
    padAngle: number;
}


export interface Pie<This, Datum> {
    (this: This, data: Array<Datum>, ...args: any[]): Array<PieArcDatum<Datum>>;
    value(): (d: Datum, i: number, data: Array<Datum>) => number;
    value(value: number): this;
    value(value: (d: Datum, i: number, data: Array<Datum>) => number): this;
    sort(): ((a: Datum, b: Datum) => number) | null;
    sort(comparator: (a: Datum, b: Datum) => number): this;
    sort(comparator: null): this;
    sortValues(): ((a: number, b: number) => number) | null;
    sortValues(comparator: (a: number, b: number) => number): this;
    sortValues(comparator: null): this;
    startAngle(): (this: This, data: Array<Datum>, ...args: any[]) => number;
    startAngle(angle: number): this;
    startAngle(angle: (this: This, data: Array<Datum>, ...args: any[]) => number): this;
    endAngle(): (this: This, data: Array<Datum>, ...args: any[]) => number;
    endAngle(angle: number): this;
    endAngle(angle: (this: This, data: Array<Datum>, ...args: any[]) => number): this;
    padAngle(): (this: This, data: Array<Datum>, ...args: any[]) => number;
    padAngle(angle: number): this;
    padAngle(angle: (this: This, data: Array<Datum>, ...args: any[]) => number): this;
}

export function pie(): Pie<any, number | { valueOf(): number }>;
export function pie<Datum>(): Pie<any, Datum>;
export function pie<This, Datum>(): Pie<This, Datum>;



export interface Line<Datum> {
    (data: Array<Datum>): string | undefined;
    x(): (d: Datum, index: number, data: Array<Datum>) => number;
    x(x: number): this;
    x(x: (d: Datum, index: number, data: Array<Datum>) => number): this;
    y(): (d: Datum, index: number, data: Array<Datum>) => number;
    y(y: number): this;
    y(y: (d: Datum, index: number, data: Array<Datum>) => number): this;
    defined(): (d: Datum, index: number, data: Array<Datum>) => boolean;
    defined(defined: boolean): this;
    defined(defined: (d: Datum, index: number, data: Array<Datum>) => boolean): this;
    curve(): CurveFactory | CurveFactoryLineOnly;
    curve(curve: CurveFactory | CurveFactoryLineOnly): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;
}
export function line(): Line<[number, number]>;
export function line<Datum>(): Line<Datum>;
export function line<This, Datum>(): Line<Datum>;

export interface RadialLine<Datum> {
    (data: Array<Datum>): string | undefined;
    angle(): (d: Datum, index: number, data: Array<Datum>) => number;
    angle(angle: number): this;
    angle(angle: (d: Datum, index: number, data: Array<Datum>) => number): this;
    radius(): (d: Datum, index: number, data: Array<Datum>) => number;
    radius(radius: number): this;
    radius(radius: (d: Datum, index: number, data: Array<Datum>) => number): this;
    defined(): (d: Datum, index: number, data: Array<Datum>) => boolean;
    defined(defined: boolean): this;
    defined(defined: (d: Datum, index: number, data: Array<Datum>) => boolean): this;
    curve(): CurveFactory | CurveFactoryLineOnly;
    curve(curve: CurveFactory | CurveFactoryLineOnly): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;
}

export function radialLine(): RadialLine<[number, number]>;
export function radialLine<Datum>(): RadialLine<Datum>;



export interface Area<Datum> {
    (data: Array<Datum>): string | undefined;
    x(): (d: Datum, index: number, data: Array<Datum>) => number;
    x(x: number): this;
    x(x: (d: Datum, index: number, data: Array<Datum>) => number): this;
    x0(): (d: Datum, index: number, data: Array<Datum>) => number;
    x0(x0: number): this;
    x0(x0: (d: Datum, index: number, data: Array<Datum>) => number): this;
    x1(): ((d: Datum, index: number, data: Array<Datum>) => number) | null;
    x1(x: number): this;
    x1(x: (d: Datum, index: number, data: Array<Datum>) => number): this;
    y(): (d: Datum, index: number, data: Array<Datum>) => number;
    y(y: number): this;
    y(y: (d: Datum, index: number, data: Array<Datum>) => number): this;
    y0(): (d: Datum, index: number, data: Array<Datum>) => number;
    y0(y: number): this;
    y0(y: (d: Datum, index: number, data: Array<Datum>) => number): this;
    y1(): (d: Datum, index: number, data: Array<Datum>) => number;
    y1(y: number): this;
    y1(y: (d: Datum, index: number, data: Array<Datum>) => number): this;
    defined(): (d: Datum, index: number, data: Array<Datum>) => boolean;
    defined(defined: boolean): this;
    defined(defined: (d: Datum, index: number, data: Array<Datum>) => boolean): this;
    curve(): CurveFactory;
    curve(curve: CurveFactory): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;
    lineX0(): Line<Datum>;
    lineY0(): Line<Datum>;
    lineX1(): Line<Datum>;
    lineY1(): Line<Datum>;
}

export function area(): Area<[number, number]>;
export function area<Datum>(): Area<Datum>;


export interface RadialArea<Datum> {
    (data: Array<Datum>): string | undefined;
    angle(): (d: Datum, index: number, data: Array<Datum>) => number;
    angle(angle: number): this;
    angle(angle: (d: Datum, index: number, data: Array<Datum>) => number): this;
    startAngle(): (d: Datum, index: number, data: Array<Datum>) => number;
    startAngle(angle: number): this;
    startAngle(angle: (d: Datum, index: number, data: Array<Datum>) => number): this;
    endAngle(): ((d: Datum, index: number, data: Array<Datum>) => number) | null;
    endAngle(angle: number): this;
    endAngle(angle: (d: Datum, index: number, data: Array<Datum>) => number): this;
    radius(): (d: Datum, index: number, data: Array<Datum>) => number;
    radius(radius: number): this;
    radius(radius: (d: Datum, index: number, data: Array<Datum>) => number): this;
    innerRadius(): (d: Datum, index: number, data: Array<Datum>) => number;
    innerRadius(radius: number): this;
    innerRadius(radius: (d: Datum, index: number, data: Array<Datum>) => number): this;
    outerRadius(): (d: Datum, index: number, data: Array<Datum>) => number;
    outerRadius(radius: number): this;
    outerRadius(radius: (d: Datum, index: number, data: Array<Datum>) => number): this;
    defined(): (d: Datum, index: number, data: Array<Datum>) => boolean;
    defined(defined: boolean): this;
    defined(defined: (d: Datum, index: number, data: Array<Datum>) => boolean): this;
    curve(): CurveFactory;
    curve(curve: CurveFactory): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;
    lineStartAngle(): RadialLine<Datum>;
    lineInnerRadius(): RadialLine<Datum>;
    lineEndAngle(): RadialLine<Datum>;
    lineOuterRadius(): RadialLine<Datum>;
}

export function radialArea(): RadialArea<[number, number]>;
export function radialArea<Datum>(): RadialArea<Datum>;


export interface CurveGeneratorLineOnly {
    lineStart(): void;
    lineEnd(): void;
    point(x: number, y: number): void;
}

export interface CurveFactoryLineOnly {
    (context: CanvasRenderingContext2D | null): CurveGeneratorLineOnly;
}

export interface CurveGenerator extends CurveGeneratorLineOnly {
    areaStart(): void;
    areaEnd(): void;
}

export interface CurveFactory {
    (context: CanvasRenderingContext2D | null): CurveGenerator;
}

export var curveBasis: CurveFactory;

export var curveBasisOpen: CurveFactory;

export var curveBasisClosed: CurveFactory;

export interface CurveBundleFactory extends CurveFactoryLineOnly {
    beta(beta: number): this;
}

export var curveBundle: CurveBundleFactory;

export interface CurveCardinalFactory extends CurveFactory {
    tension(tension: number): this;
}

export var curveCardinal: CurveCardinalFactory;
export var curveCardinalOpen: CurveCardinalFactory;
export var curveCardinalClosed: CurveCardinalFactory;

export interface CurveCatmullRomFactory extends CurveFactory {
    alpha(alpha: number): this;
}

export var curveCatmullRom: CurveCatmullRomFactory;
export var curveCatmullRomOpen: CurveCatmullRomFactory;
export var curveCatmullRomClosed: CurveCatmullRomFactory;

export var curveLinear: CurveFactory;

export var curveLinearClosed: CurveFactory;

export var curveMonotoneX: CurveFactory;

export var curveMonotoneY: CurveFactory;

export var curveNatural: CurveFactory;

export var curveStep: CurveFactory;

export var curveStepAfter: CurveFactory;

export var curveStepBefore: CurveFactory;



export interface SymbolType {
    draw(context: CanvasPathMethods, size: number): void;
}


export interface Symbol<This, Datum> {
    (this: This, d?: Datum, ...args: any[]): undefined | string;
    size(): (this: This, d: Datum, ...args: any[]) => number;
    size(size: number): this;
    size(size: (this: This, d: Datum, ...args: any[]) => number): this;
    type(): (this: This, d: Datum, ...args: any[]) => SymbolType;
    type(type: SymbolType): this;
    type(type: (this: This, d: Datum, ...args: any[]) => SymbolType): this;
    context(): CanvasRenderingContext2D | null;
    context(context: CanvasRenderingContext2D): this;
    context(context: null): this;

}

export function symbol(): Symbol<any, any>;
export function symbol<Datum>(): Symbol<any, Datum>;
export function symbol<This, Datum>(): Symbol<This, Datum>;

export var symbols: Array<SymbolType>;


export var symbolCircle: SymbolType;

export var symbolCross: SymbolType;
export var symbolDiamond: SymbolType;
export var symbolSquare: SymbolType;
export var symbolStar: SymbolType;
export var symbolTriangle: SymbolType;
export var symbolWye: SymbolType;




export interface SeriesPoint<Datum> extends Array<number> {
    0: number;
    1: number;
    index: number;
    data: Datum;
}

export interface Series<Datum, Key> extends Array<SeriesPoint<Datum>> {
    key: Key;
}

export interface Stack<This, Datum, Key> {
    (data: Array<Datum>, ...args: any[]): Array<Series<Datum, Key>>;

    keys(): (this: This, data: Array<Datum>, ...args: any[]) => Array<Key>;
    keys(keys: Array<Key>): this;
    keys(keys: (this: This, data: Array<Datum>, ...args: any[]) => Array<Key>): this;

    value(): (d: Datum, key: Key, j: number, data: Array<Datum>) => number;
    value(value: number): this;
    value(value: (d: Datum, key: Key, j: number, data: Array<Datum>) => number): this;

    order(): (series: Series<Datum, Key>) => Array<number>;
    order(order: null): this;
    order(order: Array<number>): this;
    order(order: (series: Series<Datum, Key>) => Array<number>): this;

    offset(): (series: Series<Datum, Key>, order: Array<number>) => void;
    offset(offset: null): this;
    offset(offset: (series: Series<Datum, Key>, order: Array<number>) => void): this;

}

export function stack(): Stack<any, { [key: string]: number }, string>;
export function stack<Datum>(): Stack<any, Datum, string>;
export function stack<Datum, Key>(): Stack<any, Datum, Key>;
export function stack<This, Datum, Key>(): Stack<This, Datum, Key>;


export function stackOrderAscending(series: Series<any, any>): Array<number>;
export function stackOrderDescending(series: Series<any, any>): Array<number>
export function stackOrderInsideOut(series: Series<any, any>): Array<number>
export function stackOrderNone(series: Series<any, any>): Array<number>
export function stackOrderReverse(series: Series<any, any>): Array<number>

export function stackOffsetExpand(series: Series<any, any>, order: Array<number>): void;
export function stackOffsetNone(series: Series<any, any>, order: Array<number>): void;
export function stackOffsetSilhouette(series: Series<any, any>, order: Array<number>): void;
export function stackOffsetWiggle(series: Series<any, any>, order: Array<number>): void;


export interface TimeInterval {
    (date: Date): Date;
    floor(date: Date): Date;
    round(date: Date): Date;
    ceil(date: Date): Date;
    offset(date: Date, step?: number): Date;
    range(start: Date, stop: Date, step?: number): Date[];
    filter(test: (date: Date) => boolean): this;
}

export interface CountableTimeInterval extends TimeInterval {
    count(start: Date, end: Date): number;
    every(step: number): TimeInterval | null;
}


export function timeInterval(
    floor: (date: Date) => void,
    offset: (date: Date, step: number) => void,
): TimeInterval;

export function timeInterval(
    floor: (date: Date) => void,
    offset: (date: Date, step: number) => void,
    count: (start: Date, end: Date) => number,
    field?: (date: Date) => number
): CountableTimeInterval;




export var timeMillisecond: CountableTimeInterval;
export function timeMilliseconds(start: Date, stop: Date, step?: number): Date[];

export var timeSecond: CountableTimeInterval;
export function timeSeconds(start: Date, stop: Date, step?: number): Date[];

export var timeMinute: CountableTimeInterval;
export function timeMinutes(start: Date, stop: Date, step?: number): Date[];

export var timeHour: CountableTimeInterval;
export function timeHours(start: Date, stop: Date, step?: number): Date[];

export var timeDay: CountableTimeInterval;
export function timeDays(start: Date, stop: Date, step?: number): Date[];

export var timeWeek: CountableTimeInterval;
export function timeWeeks(start: Date, stop: Date, step?: number): Date[];

export var timeSunday: CountableTimeInterval;
export function timeSundays(start: Date, stop: Date, step?: number): Date[];
export var timeMonday: CountableTimeInterval;
export function timeMondays(start: Date, stop: Date, step?: number): Date[];
export var timeTuesday: CountableTimeInterval;
export function timeTuesdays(start: Date, stop: Date, step?: number): Date[];
export var timeWednesday: CountableTimeInterval;
export function timeWednesdays(start: Date, stop: Date, step?: number): Date[];
export var timeThursday: CountableTimeInterval;
export function timeThursdays(start: Date, stop: Date, step?: number): Date[];
export var timeFriday: CountableTimeInterval;
export function timeFridays(start: Date, stop: Date, step?: number): Date[];
export var timeSaturday: CountableTimeInterval;
export function timeSaturdays(start: Date, stop: Date, step?: number): Date[];

export var timeMonth: CountableTimeInterval;
export function timeMonths(start: Date, stop: Date, step?: number): Date[];

export var timeYear: CountableTimeInterval;
export function timeYears(start: Date, stop: Date, step?: number): Date[];



export var utcMillisecond: CountableTimeInterval;
export function utcMilliseconds(start: Date, stop: Date, step?: number): Date[];

export var utcSecond: CountableTimeInterval;
export function utcSeconds(start: Date, stop: Date, step?: number): Date[];

export var utcMinute: CountableTimeInterval;
export function utcMinutes(start: Date, stop: Date, step?: number): Date[];

export var utcHour: CountableTimeInterval;
export function utcHours(start: Date, stop: Date, step?: number): Date[];

export var utcDay: CountableTimeInterval;
export function utcDays(start: Date, stop: Date, step?: number): Date[];

export var utcWeek: CountableTimeInterval;
export function utcWeeks(start: Date, stop: Date, step?: number): Date[];

export var utcSunday: CountableTimeInterval;
export function utcSundays(start: Date, stop: Date, step?: number): Date[];
export var utcMonday: CountableTimeInterval;
export function utcMondays(start: Date, stop: Date, step?: number): Date[];
export var utcTuesday: CountableTimeInterval;
export function utcTuesdays(start: Date, stop: Date, step?: number): Date[];
export var utcWednesday: CountableTimeInterval;
export function utcWednesdays(start: Date, stop: Date, step?: number): Date[];
export var utcThursday: CountableTimeInterval;
export function utcThursdays(start: Date, stop: Date, step?: number): Date[];
export var utcFriday: CountableTimeInterval;
export function utcFridays(start: Date, stop: Date, step?: number): Date[];
export var utcSaturday: CountableTimeInterval;
export function utcSaturdays(start: Date, stop: Date, step?: number): Date[];

export var utcMonth: CountableTimeInterval;
export function utcMonths(start: Date, stop: Date, step?: number): Date[];

export var utcYear: CountableTimeInterval;
export function utcYears(start: Date, stop: Date, step?: number): Date[];

/**
 * Specification of time locale to use when creating a new TimeLocaleObject
 */
export interface TimeLocaleDefinition {
    /**
     * The date and time (%c) format specifier (e.g., "%a %b %e %X %Y").
     */
    dateTime: string;
    /**
     * The date (%x) format specifier (e.g., "%m/%d/%Y").
     */
    date: string;
    /**
     *  The time (%X) format specifier (e.g., "%H:%M:%S").
     */
    time: string;
    /**
     * The A.M. and P.M. equivalents (e.g., ["AM", "PM"]).
     */
    periods: [string, string];
    /**
     * The full names of the weekdays, starting with Sunday.
     */
    days: [string, string, string, string, string, string, string];
    /**
     * The abbreviated names of the weekdays, starting with Sunday.
     */
    shortDays: [string, string, string, string, string, string, string];
    /**
     * The full names of the months (starting with January).
     */
    months: [string, string, string, string, string, string, string, string, string, string, string, string];
    /**
     * the abbreviated names of the months (starting with January).
     */
    shortMonths: [string, string, string, string, string, string, string, string, string, string, string, string];
}


export interface TimeLocaleObject {
    format(specifier: string): (date: Date) => string;
    parse(specifier: string): (dateString: string) => (Date | null);
    utcFormat(specifier: string): (date: Date) => string;
    utcParse(specifier: string): (dateString: string) => (Date | null);
}

/**
 * Create a new time-locale-based object which exposes time-formatting
 * methods for the specified locale definition.
 */
export function timeFormatLocale(timeLocale: TimeLocaleDefinition): TimeLocaleObject;

/**
 * Create a new time-locale-based object which exposes time-formatting
 * methods for the specified locale definition. The new time locale definition
 * will be set as the new default time locale.
 */
export function timeFormatDefaultLocale(defaultTimeLocale: TimeLocaleDefinition): TimeLocaleObject;

export function timeFormat(specifier: string): (date: Date) => string;

export function timeParse(specifier: string): (dateString: string) => (Date | null);

export function utcFormat(specifier: string): (date: Date) => string;

export function utcParse(specifier: string): (dateString: string) => (Date | null);



export function isoFormat(date: Date): string;

export function isoParse(dateString: string): Date;

/**
 * Returns the current time as defined by performance.now if available, and Date.now if not.
 * The current time is updated at the start of a frame; it is thus consistent during the frame, and any timers scheduled during the same frame will be synchronized.
 * If this method is called outside of a frame, such as in response to a user event, the current time is calculated and then fixed until the next frame,
 * again ensuring consistent timing during event handling.
 */
export function now(): number;


export interface Timer {
    /**
     * Restart a timer with the specified callback and optional delay and time.
     * This is equivalent to stopping this timer and creating a new timer with the specified arguments,
     * although this timer retains the original invocation priority.
     * @param callback A callback function to be invoked and passed in the apparent
     * elapsed time since the timer became active in milliseconds.
     * @param [delay] An optional numeric delay in milliseconds (default = 0) relative to time.
     * @param [time] An optional time in milliseconds relative to which the delay is calculated (default = now).
     */
    restart(callbackFn: (elapsed: number) => void, delay?: number, time?: number): void;

    /**
     * Stop the timer.
     */
    stop(): void;
}

/**
 * Schedules and returns a new timer, invoking the specified callback repeatedly until the timer is stopped.
 * The callback is passed the (apparent) elapsed time since the timer became active.
 *
 * @param callback A callback function to be invoked and passed in the apparent
 * elapsed time since the timer became active in milliseconds.
 * @param [delay] An optional numeric delay in milliseconds (default = 0) relative to time.
 * @param [time] An optional time in milliseconds relative to which the delay is calculated (default = now).
 */
export function timer(callback: (elapsed: number) => void, delay?: number, time?: number): Timer;

/**
 * Immediately invoke any eligible timer callbacks
 */
export function timerFlush(): void;

/**
 * Schedules and returns a new timer, invoking the specified callback. The timer is stopped automatically
 * on its first callback. The callback is passed the (apparent) elapsed time since the timer became active.
 *
 * @param callback A callback function to be invoked and passed in the apparent
 * elapsed time since the timer became active in milliseconds.
 * @param [delay] An optional numeric delay in milliseconds (default = 0) relative to time.
 * @param [time] An optional time in milliseconds relative to which the delay is calculated (default = now).
 */
export function timeout(callback: (elapsed: number) => void, delay?: number, time?: number): Timer;

/**
 * Schedules and returns a new timer, invoking the specified callback repeatedly every 'delay' milliseconds
 * until the timer is stopped.
 * The callback is passed the (apparent) elapsed time since the timer became active.
 *
 * @param callback A callback function to be invoked and passed in the apparent
 * elapsed time since the timer became active in milliseconds.
 * @param [delay] An optional numeric delay in milliseconds between repeat invocations of the callback.
 * If not specified, the interval timer behaves like the regular timer.
 * @param [time] An optional time in milliseconds relative to which the initial delay is calculated (default = now).
 */
export function interval(callback: (elapsed: number) => void, delay?: number, time?: number): Timer;

import { ArrayLike, BaseType, Selection, ValueFn } from '../d3-selection';

/**
 * Extend interface 'Selection' by declaration merging with 'd3-selection'
 */
declare module '../d3-selection' {
    export interface Selection<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {
        interrupt(name?: string): Transition<GElement, Datum, PElement, PDatum>;
        transition(name?: string): Transition<GElement, Datum, PElement, PDatum>;
        transition(transition: Transition<BaseType, any, any, any>): Transition<GElement, Datum, PElement, PDatum>;
    }
}


export function active<GElement extends BaseType, Datum, PElement extends BaseType, PDatum>(node: GElement, name?: string): Transition<GElement, Datum, PElement, PDatum> | null;

export function interrupt(node: BaseType, name?: string): void;

export interface Transition<GElement extends BaseType, Datum, PElement extends BaseType, PDatum> {

    // Sub-selection -------------------------

    select<DescElement extends BaseType>(selector: string): Transition<DescElement, Datum, PElement, PDatum>;
    select<DescElement extends BaseType>(selector: ValueFn<GElement, Datum, DescElement>): Transition<DescElement, Datum, PElement, PDatum>;

    // NB: while the empty selections (null or undefined selector) are defined on the underlying object, they should not be exposed in the type definition API
    // as they are meaningless on transitions.)
    // selectAll(): Transition<undefined, undefined, GElement, Datum>; // _groups are set to empty array, first generic type is set to undefined by convention
    // selectAll(selector: null): Transition<undefined, undefined, GElement, Datum>; // _groups are set to empty array, first generic type is set to undefined by convention
    selectAll<DescElement extends BaseType, OldDatum>(selector: string): Transition<DescElement, OldDatum, GElement, Datum>;
    selectAll<DescElement extends BaseType, OldDatum>(selector: ValueFn<GElement, Datum, Array<DescElement> | ArrayLike<DescElement>>): Transition<DescElement, OldDatum, GElement, Datum>;

    selection(): Selection<GElement, Datum, PElement, PDatum>;
    transition(): Transition<GElement, Datum, PElement, PDatum>;

    // Modifying -------------------------------

    attr(name: string, value: null): this;
    attr(name: string, value: string | number | boolean): this;
    attr(name: string, value: ValueFn<GElement, Datum, string | number | boolean>): this;
    attrTween(name: string, tweenFn: ValueFn<GElement, Datum, (t: number) => (string | number | boolean)>): this;

    style(name: string, value: null): this;
    style(name: string, value: string | number | boolean, priority?: null | 'important'): this;
    style(name: string, value: ValueFn<GElement, Datum, string | number | boolean>, priority?: null | 'important'): this;
    styleTween(name: string, tweenFn: ValueFn<GElement, Datum, (t: number) => (string | number | boolean)>, priority?: null | 'important'): this;

    text(value: null): this;
    text(value: string | number | boolean): this;
    text(value: ValueFn<GElement, Datum, string | number | boolean>): this;

    tween(name: string): ValueFn<GElement, Datum, (t: number) => void>;
    tween(name: string, tweenFn: null): this;
    tween(name: string, tweenFn: ValueFn<GElement, Datum, (t: number) => void>): this;

    remove(): this;

    merge(other: Transition<GElement, Datum, PElement, PDatum>): Transition<GElement, Datum, PElement, PDatum>;

    filter(filter: string): this;
    filter(filter: ValueFn<GElement, Datum, boolean>): this;

    // Event Handling -------------------

    on(type: string): ValueFn<GElement, Datum, void>;
    on(type: string, listener: null): this;
    on(type: string, listener: ValueFn<GElement, Datum, void>): this;

    // Control Flow ----------------------

    each(valueFn: ValueFn<GElement, Datum, void>): this;

    call(func: (transition: Transition<GElement, Datum, PElement, PDatum>, ...args: any[]) => any, ...args: any[]): this;

    empty(): boolean;

    node(): GElement;
    nodes(): Array<GElement>;

    size(): number;

    // Transition Configuration ----------------------

    delay(): number;
    delay(milliseconds: number): this;

    duration(): number;
    duration(milliseconds: number): this;

    ease(): (normalizedTime: number) => number;
    ease(easingFn: (normalizedTime: number) => number): this;
}


export function transition(name: string): Transition<HTMLElement, any, null, undefined>;
export function transition<GElement extends BaseType, Datum, PElement extends BaseType, PDatum>(transition: Transition<GElement, Datum, PElement, PDatum>): Transition<GElement, Datum, PElement, PDatum>;



/**
 * The VoronoiPoint interface is defined as a cue that the array is strictly of type [number, number] with two elements
 * for x and y coordinates. However, it is used as a base for interface definitions, and [number, number]
 * cannot be extended.
 */
export interface VoronoiPoint extends Array<number> {
    0: number;
    1: number;
}

/**
 * The VoronoiPointPair interface is defined as a cue that the array is strictly of type [[number, number], [number, number]] with two elements, one
 * for each point containing the respective x and y coordinates. However, it is used as a base for interface definitions, and
 * [[number, number], [number, number]] cannot be extended.
 */
export interface VoronoiPointPair extends Array<[number, number]> {
    0: [number, number];
    1: [number, number];
}

export interface VoronoiPolygon<T> extends Array<[number, number]> {
    data: T;
}

export type VoronoiTriangle<T> = [T, T, T];

export interface VoronoiSite<T> extends VoronoiPoint {
    index: number;
    data: T;
}

export interface VoronoiCell<T> {
    site: VoronoiSite<T>;
    halfEdges: Array<number>;
}



export interface VoronoiEdge<T> extends VoronoiPointPair {
    left: VoronoiSite<T>;
    right: VoronoiSite<T> | null;
}

export interface VoronoiLink<T> {
    source: T;
    target: T;
}

export interface VoronoiLayout<T> {
    (data: Array<T>): VoronoiDiagram<T>;
    x(): (d: T) => number;
    x(x: (d: T) => number): this;
    y(): (d: T) => number;
    y(y: (d: T) => number): this;
    extent(): [[number, number], [number, number]] | null;
    extent(extent: [[number, number], [number, number]]): this;
    size(): [number, number] | null;
    size(size: [number, number]): this;
    polygons(data: Array<T>): Array<VoronoiPolygon<T>>;
    triangles(data: Array<T>): Array<VoronoiTriangle<T>>;
    links(data: Array<T>): Array<VoronoiLink<T>>;
}

export interface VoronoiDiagram<T> {
    edges: Array<VoronoiEdge<T>>;
    cells: Array<VoronoiCell<T> | null>;
    polygons(): Array<VoronoiPolygon<T>>;
    triangles(): Array<VoronoiTriangle<T>>;
    links(): Array<VoronoiLink<T>>;
}


export function voronoi(): VoronoiLayout<[number, number]>;
export function voronoi<T>(): VoronoiLayout<T>;

import { ArrayLike, Selection, TransitionLike, ValueFn } from '../d3-selection';



/**
 * ZoomedElementBaseType serves as an alias for the 'minimal' data type which can be selected
 * without 'd3-zoom' (and related code in 'd3-selection') trying to use properties internally which would otherwise not
 * be supported.
 */
type ZoomedElementBaseType = Element;

/**
 * Minimal interface for a continuous scale.
 * This interface is used as a minimum contract for scale objects
 * that  can be passed into zoomTransform methods rescaleX and rescaleY
 */
export interface ZoomScale {
    domain(): Array<number>;
    domain(domain: Array<number>): this;
    range(): Array<number>;
    range(range: Array<number>): this;
    copy(): ZoomScale;
    invert(value: number): number;
}



export interface ZoomBehavior<ZoomRefElement extends ZoomedElementBaseType, Datum> extends Function {
    (selection: Selection<ZoomRefElement, Datum, any, any>, ...args: any[]): void;
    transform(selection: Selection<ZoomRefElement, Datum, any, any>, transform: ZoomTransform): void;
    transform(selection: Selection<ZoomRefElement, Datum, any, any>, transform: ValueFn<ZoomRefElement, Datum, ZoomTransform>): void;
    transform(transition: TransitionLike<ZoomRefElement, Datum>, transform: ZoomTransform): void;
    transform(transition: TransitionLike<ZoomRefElement, Datum>, transform: ValueFn<ZoomRefElement, Datum, ZoomTransform>): void;

    translateBy(selection: Selection<ZoomRefElement, Datum, any, any>, x: number, y: number): void;
    translateBy(selection: Selection<ZoomRefElement, Datum, any, any>, x: ValueFn<ZoomRefElement, Datum, number>, y: number): void;
    translateBy(selection: Selection<ZoomRefElement, Datum, any, any>, x: number, y: ValueFn<ZoomRefElement, Datum, number>): void;
    translateBy(selection: Selection<ZoomRefElement, Datum, any, any>, x: ValueFn<ZoomRefElement, Datum, number>, y: ValueFn<ZoomRefElement, Datum, number>): void;
    translateBy(transition: TransitionLike<ZoomRefElement, Datum>, x: number, y: number): void;
    translateBy(transition: TransitionLike<ZoomRefElement, Datum>, x: ValueFn<ZoomRefElement, Datum, number>, y: number): void;
    translateBy(transition: TransitionLike<ZoomRefElement, Datum>, x: number, y: ValueFn<ZoomRefElement, Datum, number>): void;
    translateBy(transition: TransitionLike<ZoomRefElement, Datum>, x: ValueFn<ZoomRefElement, Datum, number>, y: ValueFn<ZoomRefElement, Datum, number>): void;

    scaleBy(selection: Selection<ZoomRefElement, Datum, any, any>, k: number): void;
    scaleBy(selection: Selection<ZoomRefElement, Datum, any, any>, k: ValueFn<ZoomRefElement, Datum, number>): void;
    scaleBy(transition: TransitionLike<ZoomRefElement, Datum>, k: number): void;
    scaleBy(transition: TransitionLike<ZoomRefElement, Datum>, k: ValueFn<ZoomRefElement, Datum, number>): void;

    scaleTo(selection: Selection<ZoomRefElement, Datum, any, any>, k: number): void;
    scaleTo(selection: Selection<ZoomRefElement, Datum, any, any>, k: ValueFn<ZoomRefElement, Datum, number>): void;
    scaleTo(transition: TransitionLike<ZoomRefElement, Datum>, k: number): void;
    scaleTo(transition: TransitionLike<ZoomRefElement, Datum>, k: ValueFn<ZoomRefElement, Datum, number>): void;

    filter(): ValueFn<ZoomRefElement, Datum, boolean>;
    filter(filterFn: ValueFn<ZoomRefElement, Datum, boolean>): this;

    extent(): ValueFn<ZoomRefElement, Datum, [[number, number], [number, number]]>;
    extent(extent: [[number, number], [number, number]]): this;
    extent(extent: ValueFn<ZoomRefElement, Datum, [[number, number], [number, number]]>): this;

    scaleExtent(): [number, number];
    scaleExtent(extent: [number, number]): this;

    translateExtent(): [[number, number], [number, number]];
    translateExtent(extent: [[number, number], [number, number]]): this;

    duration(): number;
    duration(duration: number): this;

    on(typenames: string): ValueFn<ZoomRefElement, Datum, void>;
    on(typenames: string, callback: null): this;
    on(typenames: string, callback: ValueFn<ZoomRefElement, Datum, void>): this;
}


export function zoom<ZoomRefElement extends ZoomedElementBaseType, Datum>(): ZoomBehavior<ZoomRefElement, Datum>;



export interface D3ZoomEvent<ZoomRefElement extends ZoomedElementBaseType, Datum> {
    target: ZoomBehavior<ZoomRefElement, Datum>;
    type: 'start' | 'zoom' | 'end' | string; // Leave failsafe string type for cases like 'zoom.foo'
    transform: ZoomTransform;
    sourceEvent: any;
}



export interface ZoomTransform {
    readonly x: number;
    readonly y: number;
    readonly k: number;
    apply(point: [number, number]): [number, number];
    applyX(x: number): number;
    applyY(y: number): number;
    invert(point: [number, number]): [number, number];
    invertX(x: number): number;
    invertY(y: number): number;
    rescaleX<S extends ZoomScale>(xScale: S): S;
    rescaleY<S extends ZoomScale>(yScale: S): S;
    scale(k: number): ZoomTransform;
    toString(): string;
    translate(x: number, y: number): ZoomTransform;
}

export function zoomTransform(node: ZoomedElementBaseType): ZoomTransform;


export const zoomIdentity: ZoomTransform;
