# core/analyzers/algorithm.analyzer.ts

> 74 lines of code and 15 definitions.

Tree: GovLab Context
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/context#file-context-core-analyzers-algorithm-analyzer-ts
Source text: https://banes-lab.com/source/context/core/analyzers/algorithm.analyzer.ts.txt

Listed in [core/analyzers](https://banes-lab.com/api/source/context/core/analyzers.md), before [core/analyzers/check.analyzer.ts](https://banes-lab.com/source/context/core/analyzers/check.analyzer.ts.md).

## Definitions

- `allForces` (lexical_declaration, line 9, exported)
- `neighborsOf` (lexical_declaration, line 59)
- `isCore` (lexical_declaration, line 29, exported)
- `clustersOf` (lexical_declaration, line 25, exported)
- `concernJoinOf` (lexical_declaration, line 44, exported)
- `traverseComposes` (lexical_declaration, line 65, exported)
- `byName` (lexical_declaration, line 5)
- `degreesOf` (lexical_declaration, line 13)
- `memberIds` (lexical_declaration, line 14)
- `members` (lexical_declaration, line 27, exported)
- `degree` (lexical_declaration, line 28, exported)
- `seen` (lexical_declaration, line 69, exported)
- `order` (lexical_declaration, line 70, exported)
- `queue` (lexical_declaration, line 71, exported)
- `id` (lexical_declaration, line 73, exported)

## Contained in

- [core/analyzers](https://banes-lab.com/anatomy/context/folder-context-core-analyzers.md)

## Uses

- [core/converters/identifier.converter.ts](https://banes-lab.com/source/context/core/converters/identifier.converter.ts.md)

## Linked from

- [core/analyzers](https://banes-lab.com/anatomy/context/folder-context-core-analyzers.md)
- [core/converters](https://banes-lab.com/anatomy/context/folder-context-core-converters.md)

## Source

```typescript
import type { Cluster, ConcernJoinRow, ConcernResolvers, Contract } from "#types/algorithm.types";
import { CORE_DEGREE } from "#configuration/constants/algorithm.constants";
import { slugify } from "#core/converters/identifier.converter";

const byName = function byName(a: string, b: string): number {
    return a.localeCompare(b);
};

export const allForces = function allForces(contracts: readonly Contract[]): string[] {
    return [...new Set(contracts.flatMap((contract) => contract.force))].toSorted(byName);
};

const degreesOf = function degreesOf(members: readonly Contract[]): Map<string, number> {
    const memberIds = new Set(members.map((contract) => contract.id));
    const degree = new Map<string, number>();
    for (const contract of members) {
        for (const target of contract.composes.filter((id) => memberIds.has(id))) {
            degree.set(contract.id, (degree.get(contract.id) ?? 0) + 1);
            degree.set(target, (degree.get(target) ?? 0) + 1);
        }
    }
    return degree;
};

export const clustersOf = function clustersOf(contracts: readonly Contract[]): Cluster[] {
    return allForces(contracts).map((force) => {
        const members = contracts.filter((contract) => contract.force.includes(force));
        const degree = degreesOf(members);
        const isCore = (contract: Contract): boolean => (degree.get(contract.id) ?? 0) >= CORE_DEGREE;
        return {
            core: members
                .filter(isCore)
                .map((contract) => contract.id)
                .toSorted(byName),
            force,
            supporting: members
                .filter((contract) => !isCore(contract))
                .map((contract) => contract.id)
                .toSorted(byName),
        };
    });
};

export const concernJoinOf = function concernJoinOf(
    contracts: readonly Contract[],
    resolvers: ConcernResolvers,
): ConcernJoinRow[] {
    return allForces(contracts).map((force) => ({
        concerns: resolvers.concernsForForce?.(force) ?? [],
        contracts: contracts
            .filter((contract) => contract.force.includes(force))
            .map((contract) => contract.id)
            .toSorted(byName),
        force,
        principles: resolvers.principlesForForce?.(force) ?? [],
    }));
};

const neighborsOf = function neighborsOf(byId: ReadonlyMap<string, Contract>, id: string, seen: Set<string>): string[] {
    return (byId.get(id)?.composes ?? [])
        .map((target) => slugify(target))
        .filter((resolved) => byId.has(resolved) && !seen.has(resolved));
};

export const traverseComposes = function traverseComposes(
    byId: ReadonlyMap<string, Contract>,
    seed: readonly string[],
): string[] {
    const seen = new Set<string>();
    const order: string[] = [];
    const queue = [...seed];
    while (queue.length > 0) {
        const id = queue.shift();
        if (typeof id === "string" && !seen.has(id)) {
            seen.add(id);
            order.push(id);
            queue.push(...neighborsOf(byId, id, seen));
        }
    }
    return order;
};
```
