- Core calculations (Life Path, Expression, Soul Urge, Birthday) - Advanced numbers (Maturity, Personality, Hidden Passion, Karmic Lessons) - Timing cycles and optimal days finder - Compatibility analysis and name optimizer - Telos integration for personal development - Professional PDF report generation - Profile management system - Security fix: Add .claude/ to .gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
175 lines
4.7 KiB
TypeScript
175 lines
4.7 KiB
TypeScript
/**
|
|
* Core Numerology Calculator
|
|
* Shared functions for calculating Life Path, Expression, Soul Urge, Birthday
|
|
*/
|
|
|
|
const MASTER_NUMBERS = [11, 22, 33];
|
|
|
|
export function reduce(n: number): number {
|
|
if (MASTER_NUMBERS.includes(n)) return n;
|
|
const total = String(n).split('').reduce((sum, digit) => sum + parseInt(digit), 0);
|
|
return total < 10 ? total : reduce(total);
|
|
}
|
|
|
|
function nameToNumber(name: string): number {
|
|
const cleaned = name.toLowerCase().replace(/[^a-z]/g, '');
|
|
let total = 0;
|
|
for (const char of cleaned) {
|
|
const value = char.charCodeAt(0) - 96;
|
|
total += value < 10 ? value : reduce(value);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
function masterCheck(total: number): number {
|
|
if (MASTER_NUMBERS.includes(total)) return total;
|
|
const reduced = String(total).split('').reduce((sum, digit) => sum + parseInt(digit), 0);
|
|
return reduced < 10 ? reduced : reduce(reduced);
|
|
}
|
|
|
|
function extractVowels(name: string): string {
|
|
return name.toLowerCase().replace(/[^aeiou]/g, '');
|
|
}
|
|
|
|
function extractConsonants(name: string): string {
|
|
return name.toLowerCase().replace(/[^bcdfghjklmnpqrstvwxyz]/g, '');
|
|
}
|
|
|
|
function letterToNumber(letter: string): number {
|
|
const value = letter.toLowerCase().charCodeAt(0) - 96;
|
|
return value;
|
|
}
|
|
|
|
export interface CoreNumbers {
|
|
lifePath: number;
|
|
expression: number;
|
|
soulUrge: number;
|
|
birthday: number;
|
|
}
|
|
|
|
export interface AdditionalNumbers {
|
|
maturity: number;
|
|
personality: number;
|
|
hiddenPassion: number | null;
|
|
hiddenPassionCount: number;
|
|
karmicLessons: number[];
|
|
balance: number;
|
|
}
|
|
|
|
export function calculateCoreNumbers(name: string, birthdate: string): CoreNumbers {
|
|
// Parse birthdate
|
|
const parts = birthdate.split('/');
|
|
const month = parseInt(parts[0]);
|
|
const day = parseInt(parts[1]);
|
|
const year = parseInt(parts[2]);
|
|
|
|
// Life Path
|
|
const monthReduced = reduce(month);
|
|
const dayReduced = reduce(day);
|
|
const yearReduced = reduce(year);
|
|
const birthDateSum = monthReduced + dayReduced + yearReduced;
|
|
const lifePath = reduce(birthDateSum);
|
|
|
|
// Birthday
|
|
const birthday = reduce(day);
|
|
|
|
// Expression (from full name)
|
|
const nameParts = name.trim().split(/\s+/);
|
|
const namesToUse = nameParts.length > 4
|
|
? [nameParts[0], nameParts[nameParts.length - 1]]
|
|
: nameParts;
|
|
|
|
let expressionTotal = 0;
|
|
for (const part of namesToUse) {
|
|
const value = nameToNumber(part);
|
|
const checked = masterCheck(value);
|
|
expressionTotal += checked;
|
|
}
|
|
const expression = masterCheck(reduce(expressionTotal));
|
|
|
|
// Soul Urge (from vowels)
|
|
let soulUrgeTotal = 0;
|
|
for (const part of nameParts) {
|
|
const vowels = extractVowels(part);
|
|
if (vowels) {
|
|
const value = nameToNumber(vowels);
|
|
const checked = masterCheck(value);
|
|
soulUrgeTotal += checked;
|
|
}
|
|
}
|
|
const soulUrge = masterCheck(reduce(soulUrgeTotal));
|
|
|
|
return { lifePath, expression, soulUrge, birthday };
|
|
}
|
|
|
|
export function calculateAdditionalNumbers(
|
|
name: string,
|
|
coreNumbers: CoreNumbers
|
|
): AdditionalNumbers {
|
|
const nameParts = name.trim().split(/\s+/);
|
|
const fullName = name.trim();
|
|
|
|
// 1. Maturity Number (Life Path + Expression)
|
|
const maturitySum = coreNumbers.lifePath + coreNumbers.expression;
|
|
const maturity = reduce(maturitySum);
|
|
|
|
// 2. Personality Number (consonants in full name)
|
|
let personalityTotal = 0;
|
|
for (const part of nameParts) {
|
|
const consonants = extractConsonants(part);
|
|
if (consonants) {
|
|
const value = nameToNumber(consonants);
|
|
const checked = masterCheck(value);
|
|
personalityTotal += checked;
|
|
}
|
|
}
|
|
const personality = masterCheck(reduce(personalityTotal));
|
|
|
|
// 3. Hidden Passion Number (most frequent letter value)
|
|
const letterCounts: Record<number, number> = {};
|
|
const cleanedName = fullName.toLowerCase().replace(/[^a-z]/g, '');
|
|
|
|
for (const char of cleanedName) {
|
|
const num = letterToNumber(char);
|
|
const reduced = reduce(num);
|
|
letterCounts[reduced] = (letterCounts[reduced] || 0) + 1;
|
|
}
|
|
|
|
let hiddenPassion: number | null = null;
|
|
let hiddenPassionCount = 0;
|
|
|
|
for (const [num, count] of Object.entries(letterCounts)) {
|
|
if (count > hiddenPassionCount) {
|
|
hiddenPassionCount = count;
|
|
hiddenPassion = parseInt(num);
|
|
}
|
|
}
|
|
|
|
// 4. Karmic Lessons (missing numbers 1-9)
|
|
const karmicLessons: number[] = [];
|
|
for (let i = 1; i <= 9; i++) {
|
|
if (!letterCounts[i]) {
|
|
karmicLessons.push(i);
|
|
}
|
|
}
|
|
|
|
// 5. Balance Number (first letter of each name part)
|
|
let balanceTotal = 0;
|
|
for (const part of nameParts) {
|
|
if (part.length > 0) {
|
|
const firstLetter = part[0];
|
|
const num = letterToNumber(firstLetter);
|
|
balanceTotal += num;
|
|
}
|
|
}
|
|
const balance = reduce(balanceTotal);
|
|
|
|
return {
|
|
maturity,
|
|
personality,
|
|
hiddenPassion,
|
|
hiddenPassionCount,
|
|
karmicLessons,
|
|
balance
|
|
};
|
|
}
|