core/analyzers/information.analyzer.ts

core/analyzers/information.analyzer.ts is a file in GovLab Patterns. 58 lines of code and 19 definitions.

import type { Compressibility } from "#types/information.types";
import { sumOf } from "#core/counters/base.counter";

const BITS_PER_BYTE = 8;

export const entropyBits = function entropyBits(counts: Iterable<number>, total: number): number {
    if (total === 0) {
        return 0;
    }
    let entropy = 0;
    for (const count of counts) {
        const probability = count / total;
        entropy -= probability * Math.log2(probability);
    }
    return entropy;
};

const ratioOf = function ratioOf(transitions: ReadonlyMap<string, Map<string, number>>): number {
    let weighted = 0;
    let total = 0;
    for (const row of transitions.values()) {
        const rowTotal = sumOf(row.values());
        weighted += rowTotal * entropyBits(row.values(), rowTotal);
        total += rowTotal;
    }
    return total === 0 ? 0 : Math.min(1, weighted / total / BITS_PER_BYTE);
};

const rowFor = function rowFor(transitions: Map<string, Map<string, number>>, key: string): Map<string, number> {
    const existing = transitions.get(key);
    if (existing) {
        return existing;
    }
    const created = new Map<string, number>();
    transitions.set(key, created);
    return created;
};

export const createCompressibility = function createCompressibility(): Compressibility {
    const transitions = new Map<string, Map<string, number>>();
    let previous = "";
    let started = false;
    const observe = function observe(symbol: string): void {
        if (started) {
            const row = rowFor(transitions, previous);
            row.set(symbol, (row.get(symbol) ?? 0) + 1);
        }
        previous = symbol;
        started = true;
    };
    return {
        push(...chunks: readonly string[]): void {
            for (const text of chunks) {
                for (const symbol of text) {
                    observe(symbol);
                }
            }
        },
        ratio(): number {
            return ratioOf(transitions);
        },
    };
};