/ concept-collection / ans-visualizer
concept-collection / ans-visualizer
ans-visualizer / src / utils / ansLogic.ts
168 lines · 4.8 KBBlameHistoryRaw
1import type { Symbol, StateInfo } from '../types';
3/**
4 * Generate the repeating pattern of symbols based on their frequencies.
5 * For example: A-2, B-1, C-1 produces [A, A, B, C, A, A, B, C, ...]
6 */
7export function generateStatePattern(symbols: Symbol[], maxStates: number = 1000): StateInfo[] {
8 if (symbols.length === 0) return [];
9
10 // Create the base pattern by repeating each symbol according to its frequency
11 const basePattern: Symbol[] = [];
12 symbols.forEach(symbol => {
13 for (let i = 0; i < symbol.frequency; i++) {
14 basePattern.push(symbol);
15 }
16 });
18 // Generate states by repeating the base pattern
19 const states: StateInfo[] = [];
20 for (let i = 0; i < maxStates; i++) {
21 const patternIndex = i % basePattern.length;
22 states.push({
23 index: i,
24 symbol: basePattern[patternIndex]
25 });
26 }
28 return states;
31/**
32 * Calculate L (sum of all frequencies)
33 */
34export function calculateL(symbols: Symbol[]): number {
35 return symbols.reduce((sum, symbol) => sum + symbol.frequency, 0);
38/**
39 * Calculate the chain of previous states from state n back to state 0.
40 * Uses the formula: prevState = (n // L) * f_{s(n)} + (n mod L) - C_{s(n)}
41 * where:
42 * - n // L is integer division
43 * - n mod L is the remainder
44 * - s(n) is the symbol at state n
45 * - f_s is the frequency of symbol s
46 * - C_s is the cumulative sum of frequencies up to (not including) symbol s
47 */
48export function calculateEdgeChain(n: number, symbols: Symbol[], numLeadingAs: number): number[] {
49 if (symbols.length === 0 || n < 0) return [];
51 const L = calculateL(symbols);
53 // Build cumulative sum map: symbol name -> cumulative sum
54 const cumulativeMap = new Map<string, number>();
55 let cumSum = 0;
56 symbols.forEach(symbol => {
57 cumulativeMap.set(symbol.name, cumSum);
58 cumSum += symbol.frequency;
59 });
61 // Build frequency map: symbol name -> frequency
62 const frequencyMap = new Map<string, number>();
63 symbols.forEach(symbol => {
64 frequencyMap.set(symbol.name, symbol.frequency);
65 });
67 const symbolMap = new Map<number, Symbol>();
68 let ii = 0;
69 for (let i = 0; i < symbols.length; i++) {
70 for (let j = 0; j < symbols[i].frequency; j++) {
71 symbolMap.set(ii, symbols[i]);
72 ii++;
73 }
74 }
75 if (ii !== L) {
76 throw new Error("Symbol map construction error");
77 }
79 const chain: number[] = [];
80 let current = n;
82 // Iterate backwards until we reach initial states
83 while (true) {
84 if (current < (frequencyMap.get(symbols[0].name) || 1)) {
85 for (let i = 0; i < numLeadingAs; i++) {
86 chain.push(current);
87 }
88 break;
89 }
90 else {
91 chain.push(current);
92 }
94 const currentModL = current % L;
95 const currentSymbol = symbolMap.get(currentModL);
96 if (!currentSymbol) {
97 throw new Error("Invalid state encountered in edge chain calculation");
98 }
99 const symbolName = currentSymbol.name;
100 const f_s = frequencyMap.get(symbolName) || 0;
101 const C_s = cumulativeMap.get(symbolName) || 0;
103 // Calculate prevState using the formula
104 const prevState = Math.floor(current / L) * f_s + (current % L) - C_s;
106 current = prevState;
107 }
109 return chain;
112/**
113 * Calculate the forward edges from state n for each symbol.
114 * Uses the formula: nextState(n, s) = (n // f_s)*L + C_s + (n mod f_s)
115 * where:
116 * - n // f_s is integer division
117 * - f_s is the frequency of symbol s
118 * - C_s is the cumulative sum of frequencies up to (not including) symbol s
119 * - L is the sum of all frequencies
120 */
121export function calculateForwardEdges(n: number, symbols: Symbol[], maxStates: number): Array<{ toState: number; symbol: Symbol }> {
122 if (symbols.length === 0 || n < 0) return [];
124 const L = calculateL(symbols);
126 // Build cumulative sum map: symbol name -> cumulative sum
127 const cumulativeMap = new Map<string, number>();
128 let cumSum = 0;
129 symbols.forEach(symbol => {
130 cumulativeMap.set(symbol.name, cumSum);
131 cumSum += symbol.frequency;
132 });
134 const forwardEdges: Array<{ toState: number; symbol: Symbol }> = [];
136 symbols.forEach(symbol => {
137 const f_s = symbol.frequency;
138 const C_s = cumulativeMap.get(symbol.name) || 0;
140 // Calculate nextState using the formula
141 const nextState = Math.floor(n / f_s) * L + C_s + (n % f_s);
143 // Only add if the next state is within bounds
144 if (nextState >= 0 && nextState < maxStates) {
145 forwardEdges.push({ toState: nextState, symbol });
146 }
147 });
149 return forwardEdges;
152/**
153 * Get default color palette - softer, more appealing colors
154 */
155export function getDefaultColors(): string[] {
156 return [
157 '#64B5F6', // light blue
158 '#F06292', // light pink
159 '#BA68C8', // light purple
160 '#FFB74D', // light orange
161 '#81C784', // light green
162 '#E57373', // light red
163 '#4DD0E1', // light cyan
164 '#9575CD', // medium purple
165 '#FF8A65', // coral
166 '#A1887F', // light brown
167 ];