<commit_before>import {Node} from "./node";
import {Scene} from "./scene";
import {Group} from "./group";

type ValueFn<P, GDatum, PDatum> = (parent: P, data: PDatum, index: number, groups: (P | undefined)[]) => GDatum[];
type KeyFn<N, G, GDatum> = (node: N, datum: GDatum, index: number, groups: (G | undefined)[]) => string;
type NodeCallback<G, GDatum> = (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => void;
type NodeSelector<N, G, GDatum> = (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => N;
type NodeSelectorAll<N, G, GDatum> = (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => N[];

export class EnterNode {

    constructor(parent: Node | EnterNode, datum: any) {
        this.scene = parent.scene;
        this.parent = parent;
        this.datum = datum;
    }

    scene: Scene | null;
    parent: Node | EnterNode;
    datum: any;
    next: Node | EnterNode | null = null;

    appendChild<T extends Node>(node: T): T {
        if (!Group.isGroup(this.parent)) {
            throw new Error(`${this.parent} cannot have any children.`);
        }
        // if (!Node.isNode(this.next)) {
        //     throw new Error(`${this.next} cannot be appended to ${this.parent}.`);
        // }
        if (this.next && !Node.isNode(this.next)) {
            throw new Error(`${this.next} is not a Node.`);
        }
        return this.parent.insertBefore(node, this.next);
    }

    insertBefore<T extends Node>(node: T, nextNode?: Node | null): T {
        if (!Group.isGroup(this.parent)) {
            throw new Error(`${this.parent} cannot have any children.`);
        }
        return this.parent.insertBefore(node, nextNode);
    }
}

/**
 * G - type of the selected node(s).
 * GDatum - type of the datum of the selected node(s).
 * P - type of the parent node(s).
 * PDatum - type of the datum of the parent node(s).
 */
export class Selection<G extends Node | EnterNode, P extends Node | EnterNode, GDatum = any, PDatum = any> {

    constructor(groups: (G | undefined)[][], parents: (P | undefined)[]) {
        this.groups = groups;
        this.parents = parents;
    }

    groups: (G | undefined)[][];
    parents: (P | undefined)[];

    private static root = [undefined];

    static select<T extends Node>(node: T | (() => T)) {
        return new Selection([[typeof node === 'function' ? node() : node]], Selection.root);
    }

    static selectAll<G extends Node>(nodes?: G[] | null) {
        return new Selection([nodes == null ? [] : nodes], Selection.root);
    }

    append<N extends Node>(Class: new () => N): Selection<N, G, GDatum, GDatum> {
        return this.select<N>(node => {
            return node.appendChild(new Class());
        });
    }

    appendFn<N extends Node>(creator: NodeSelector<N, G, GDatum>): Selection<N, G, GDatum, GDatum> {
        return this.select<N>((node, data, index, group) => {
            return node.appendChild(creator(node, data, index, group));
        });
    }

    /**
     * Runs the given selector that returns a single node for every node in each group.
     * The original nodes are then replaced by the nodes returned by the selector
     * and returned as a new selection.
     * The selected nodes inherit the datums and the parents of the original nodes.
     */
    select<N extends Node>(selector: (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => N): Selection<N, G, GDatum, GDatum> {
        const groups = this.groups;
        const numGroups = groups.length;

        const subgroups: N[][] = [];

        for (let j = 0; j < numGroups; j++) {
            const group = groups[j];
            const groupSize = group.length;
            const subgroup = subgroups[j] = new Array<N>(groupSize);

            for (let i = 0; i < groupSize; i++) {
                const node = group[i];

                if (node) {
                    const subnode = selector(node, node.datum, i, group);

                    if (subnode) {
                        subnode.datum = node.datum;
                    }
                    subgroup[i] = subnode;
                }
                // else this can be a group of the `enter` selection,
                // for example, with no nodes at the i-th position,
                // only nodes at the end of the group
            }
        }

        return new Selection<N, G, GDatum, GDatum>(subgroups, this.parents as (G | undefined)[]);
    }

    private selectNone(): [] {
        return [];
    }

    /**
     * Runs the given selector that returns a group of nodes for every node in each group.
     * The original nodes are then replaced by the groups of nodes returned by the selector<commit_msg>Selection: new 'selectByClass' method, various fixes.<commit_after>import {Node} from "./node";
import {Scene} from "./scene";
import {Group} from "./group";

type ValueFn<P, GDatum, PDatum> = (parent: P, data: PDatum, index: number, groups: (P | undefined)[]) => GDatum[];
type KeyFn<N, G, GDatum> = (node: N, datum: GDatum, index: number, groups: (G | undefined)[]) => string;
type NodeCallback<G, GDatum> = (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => void;
type NodeSelector<N, G, GDatum> = (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => N;
type NodeSelectorAll<N, G, GDatum> = (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => N[];

export class EnterNode {

    constructor(parent: Node | EnterNode, datum: any) {
        this.scene = parent.scene;
        this.parent = parent;
        this.datum = datum;
    }

    scene: Scene | null;
    parent: Node | EnterNode;
    datum: any;
    next: Node | EnterNode | null = null;

    appendChild<T extends Node>(node: T): T {
        if (!Group.isGroup(this.parent)) {
            throw new Error(`${this.parent} cannot have any children.`);
        }
        // if (!Node.isNode(this.next)) {
        //     throw new Error(`${this.next} cannot be appended to ${this.parent}.`);
        // }
        if (this.next && !Node.isNode(this.next)) {
            throw new Error(`${this.next} is not a Node.`);
        }
        return this.parent.insertBefore(node, this.next);
    }

    insertBefore<T extends Node>(node: T, nextNode?: Node | null): T {
        if (!Group.isGroup(this.parent)) {
            throw new Error(`${this.parent} cannot have any children.`);
        }
        return this.parent.insertBefore(node, nextNode);
    }
}

/**
 * G - type of the selected node(s).
 * GDatum - type of the datum of the selected node(s).
 * P - type of the parent node(s).
 * PDatum - type of the datum of the parent node(s).
 */
export class Selection<G extends Node | EnterNode, P extends Node | EnterNode, GDatum = any, PDatum = any> {

    constructor(groups: (G | undefined)[][], parents: (P | undefined)[]) {
        this.groups = groups;
        this.parents = parents;
    }

    groups: (G | undefined)[][];
    parents: (P | undefined)[];

    private static root = [undefined];

    static select<G extends Node, P extends Node | EnterNode>(node: G | (() => G)) {
        return new Selection<G, P>([[typeof node === 'function' ? node() : node]], Selection.root);
    }

    static selectAll<G extends Node>(nodes?: G[] | null) {
        return new Selection([nodes == null ? [] : nodes], Selection.root);
    }

    append<N extends Node>(Class: new () => N): Selection<N, G, GDatum, GDatum> {
        return this.select<N>(node => {
            return node.appendChild(new Class());
        });
    }

    appendFn<N extends Node>(creator: NodeSelector<N, G, GDatum>): Selection<N, G, GDatum, GDatum> {
        return this.select<N>((node, data, index, group) => {
            return node.appendChild(creator(node, data, index, group));
        });
    }

    /**
     * Runs the given selector that returns a single node for every node in each group.
     * The original nodes are then replaced by the nodes returned by the selector
     * and returned as a new selection.
     * The selected nodes inherit the datums and the parents of the original nodes.
     */
    select<N extends Node>(selector: (node: G, datum: GDatum, index: number, group: (G | undefined)[]) => N | undefined): Selection<N, G, GDatum, GDatum> {
        const groups = this.groups;
        const numGroups = groups.length;

        const subgroups: (N | undefined)[][] = [];

        for (let j = 0; j < numGroups; j++) {
            const group = groups[j];
            const groupSize = group.length;
            const subgroup = subgroups[j] = new Array<N | undefined>(groupSize);

            for (let i = 0; i < groupSize; i++) {
                const node = group[i];

                if (node) {
                    const subnode = selector(node, node.datum, i, group);

                    if (subnode) {
                        subnode.datum = node.datum;
                    }
                    subgroup[i] = subnode;
                }
                // else this can be a group of the `enter` selection,
                // for example, with no nodes at the i-th position,
                // only nodes at the end of the group
            }
        }

        return new Selection<N, G, GDatum, GDatum>(subgroups, this.parents as (G | undefined)[]);
    }

    selectByClass<N extends Node>(Class: new () => N): Selection<N, G, GDatum, GDatum> {
        return this.select(node => {
            if (Node.isNode(node)) {
                const children = node.children;
                const n = children.length;

                for (let i = 0; i < n; i++) {
                    const child = children[i];
                    if (child instanceof Class) {
                        return child;
                    }
                }
            }
        });
    }

    private selectNone(): [] {
        return [];
    }

    /**
     * Runs the given selector that returns a group of nodes for every node in each group.
     * The original nodes are then replaced by the groups of nodes returned by the selector
{'fraction_non_alphanumeric': 0.1395163697880386, 'fraction_numerical': 0.0004975619464623346, 'mean_word_length': 2.2108626198083066, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 70, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6538109', 'n_tokens_mistral': 3021, 'n_tokens_neox': 2938, 'n_words': 1003}