import { SequencePattern } from '../types.js';
/**
* Recognizes patterns in number sequences
*/
export class SequencePatternRecognizer {
/**
* Detect pattern in a sequence
* @param sequence Array of numbers
* @returns Detected pattern or null
*/
detectPattern(sequence: number[]): SequencePattern | null {
if (sequence.length < 2) {
return null;
}
// Check different pattern types in order of complexity
const patterns: SequencePattern[] = [];
// Check for arithmetic sequence
const arithmetic = this.checkArithmeticSequence(sequence);
if (arithmetic) patterns.push(arithmetic);
// Check for geometric sequence
const geometric = this.checkGeometricSequence(sequence);
if (geometric) patterns.push(geometric);
// Check for quadratic sequence
const quadratic = this.checkQuadraticSequence(sequence);
if (quadratic) patterns.push(quadratic);
// Check for Fibonacci pattern
const fibonacci = this.checkFibonacciPattern(sequence);
if (fibonacci) patterns.push(fibonacci);
// Return the pattern with highest confidence
return patterns.reduce((best, current) =>
current.confidence > (best?.confidence || 0) ? current : best
, null as SequencePattern | null);
}
/**
* Predict the next value in a sequence
* @param sequence Array of numbers
* @param pattern Detected pattern
* @returns Predicted next value
*/
predictNext(sequence: number[], pattern: SequencePattern): number {
const n = sequence.length;
switch (pattern.type) {
case 'arithmetic':
// Next value = last value + difference
const diff = pattern.parameters[0];
return sequence[n - 1] + diff;
case 'geometric':
// Next value = last value * ratio
const ratio = pattern.parameters[0];
return sequence[n - 1] * ratio;
case 'quadratic':
// Next value = a*n² + b*n + c
const [a, b, c] = pattern.parameters;
return a * (n + 1) * (n + 1) + b * (n + 1) + c;
case 'fibonacci':
// Next value = sum of last two values
return sequence[n - 1] + sequence[n - 2];
default:
throw new Error(`Cannot predict next value for pattern type: ${pattern.type}`);
}
}
/**
* Check if sequence follows arithmetic pattern
* @param sequence Array of numbers
* @returns Arithmetic pattern or null
*/
private checkArithmeticSequence(sequence: number[]): SequencePattern | null {
if (sequence.length < 2) return null;
const differences: number[] = [];
for (let i = 1; i < sequence.length; i++) {
differences.push(sequence[i] - sequence[i - 1]);
}
// Check if all differences are the same
const allSame = differences.every(diff =>
Math.abs(diff - differences[0]) < 1e-10
);
if (allSame) {
return {
type: 'arithmetic',
parameters: [differences[0]],
confidence: 1.0,
formula: `a_n = ${sequence[0]} + ${differences[0]} * (n - 1)`
};
}
return null;
}
/**
* Check if sequence follows geometric pattern
* @param sequence Array of numbers
* @returns Geometric pattern or null
*/
private checkGeometricSequence(sequence: number[]): SequencePattern | null {
if (sequence.length < 2) return null;
const ratios: number[] = [];
for (let i = 1; i < sequence.length; i++) {
if (sequence[i - 1] === 0) return null; // Avoid division by zero
ratios.push(sequence[i] / sequence[i - 1]);
}
// Check if all ratios are the same
const allSame = ratios.every(ratio =>
Math.abs(ratio - ratios[0]) < 1e-10
);
if (allSame) {
return {
type: 'geometric',
parameters: [ratios[0]],
confidence: 1.0,
formula: `a_n = ${sequence[0]} * ${ratios[0]}^(n - 1)`
};
}
return null;
}
/**
* Check if sequence follows quadratic pattern
* @param sequence Array of numbers
* @returns Quadratic pattern or null
*/
private checkQuadraticSequence(sequence: number[]): SequencePattern | null {
if (sequence.length < 3) return null;
// Get first differences
const firstDiffs: number[] = [];
for (let i = 1; i < sequence.length; i++) {
firstDiffs.push(sequence[i] - sequence[i - 1]);
}
// Get second differences
const secondDiffs: number[] = [];
for (let i = 1; i < firstDiffs.length; i++) {
secondDiffs.push(firstDiffs[i] - firstDiffs[i - 1]);
}
// Check if second differences are constant
const allSame = secondDiffs.every(diff =>
Math.abs(diff - secondDiffs[0]) < 1e-10
);
if (allSame) {
// Calculate quadratic coefficients
const a = secondDiffs[0] / 2;
const b = firstDiffs[0] - a;
const c = sequence[0];
return {
type: 'quadratic',
parameters: [a, b, c],
confidence: 1.0,
formula: `a_n = ${a}*n² + ${b}*n + ${c}`
};
}
return null;
}
/**
* Check if sequence follows Fibonacci pattern
* @param sequence Array of numbers
* @returns Fibonacci pattern or null
*/
private checkFibonacciPattern(sequence: number[]): SequencePattern | null {
if (sequence.length < 3) return null;
let isFibonacci = true;
for (let i = 2; i < sequence.length; i++) {
const expected = sequence[i - 1] + sequence[i - 2];
if (Math.abs(sequence[i] - expected) > 1e-10) {
isFibonacci = false;
break;
}
}
if (isFibonacci) {
return {
type: 'fibonacci',
parameters: [],
confidence: 1.0,
formula: `a_n = a_(n-1) + a_(n-2)`
};
}
return null;
}
}