"use strict";
/**
 * Vector Utilities
 *
 * Utility functions for working with vector embeddings
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.dotProduct = dotProduct;
exports.magnitude = magnitude;
exports.cosineSimilarity = cosineSimilarity;
exports.euclideanDistance = euclideanDistance;
exports.normalizeVector = normalizeVector;
exports.averageVectors = averageVectors;
exports.weightedAverageVectors = weightedAverageVectors;
exports.findNearestNeighbors = findNearestNeighbors;
exports.kMeansClustering = kMeansClustering;
/**
 * Calculate the dot product of two vectors
 */
function dotProduct(vecA, vecB) {
    if (vecA.length !== vecB.length) {
        throw new Error('Vectors must have the same length');
    }
    return vecA.reduce((sum, val, i) => sum + val * vecB[i], 0);
}
/**
 * Calculate the magnitude (length) of a vector
 */
function magnitude(vec) {
    return Math.sqrt(vec.reduce((sum, val) => sum + val * val, 0));
}
/**
 * Calculate the cosine similarity between two vectors
 * Returns a value between -1 and 1, where 1 means identical direction,
 * 0 means orthogonal (perpendicular), and -1 means opposite direction
 */
function cosineSimilarity(vecA, vecB) {
    if (vecA.length !== vecB.length) {
        throw new Error('Vectors must have the same length');
    }
    const dot = dotProduct(vecA, vecB);
    const magA = magnitude(vecA);
    const magB = magnitude(vecB);
    // Prevent division by zero
    if (magA === 0 || magB === 0) {
        return 0;
    }
    return dot / (magA * magB);
}
/**
 * Calculate the Euclidean distance between two vectors
 * Lower values indicate greater similarity
 */
function euclideanDistance(vecA, vecB) {
    if (vecA.length !== vecB.length) {
        throw new Error('Vectors must have the same length');
    }
    return Math.sqrt(vecA.reduce((sum, val, i) => sum + Math.pow(val - vecB[i], 2), 0));
}
/**
 * Normalize a vector to unit length
 */
function normalizeVector(vec) {
    const mag = magnitude(vec);
    // Prevent division by zero
    if (mag === 0) {
        return vec.map(() => 0);
    }
    return vec.map(val => val / mag);
}
/**
 * Average multiple vectors into a single vector
 * Useful for creating document or concept embeddings from multiple sentences
 */
function averageVectors(vectors) {
    if (vectors.length === 0) {
        throw new Error('Cannot average empty vector array');
    }
    const vecLength = vectors[0].length;
    // Check that all vectors have the same length
    if (!vectors.every(vec => vec.length === vecLength)) {
        throw new Error('All vectors must have the same length');
    }
    // Initialize result vector with zeros
    const result = new Array(vecLength).fill(0);
    // Sum up all vectors
    for (const vec of vectors) {
        for (let i = 0; i < vecLength; i++) {
            result[i] += vec[i];
        }
    }
    // Divide by count to get average
    return result.map(val => val / vectors.length);
}
/**
 * Calculate the weighted average of multiple vectors
 * Weights should sum to 1.0
 */
function weightedAverageVectors(vectors, weights) {
    if (vectors.length === 0) {
        throw new Error('Cannot average empty vector array');
    }
    if (vectors.length !== weights.length) {
        throw new Error('Number of vectors must match number of weights');
    }
    const vecLength = vectors[0].length;
    // Check that all vectors have the same length
    if (!vectors.every(vec => vec.length === vecLength)) {
        throw new Error('All vectors must have the same length');
    }
    // Initialize result vector with zeros
    const result = new Array(vecLength).fill(0);
    // Calculate weighted sum
    for (let i = 0; i < vectors.length; i++) {
        const vec = vectors[i];
        const weight = weights[i];
        for (let j = 0; j < vecLength; j++) {
            result[j] += vec[j] * weight;
        }
    }
    return result;
}
/**
 * Find the nearest neighbors to a query vector
 * Returns indices of the nearest neighbors in descending order of similarity
 */
function findNearestNeighbors(queryVector, vectorDatabase, k = 5, similarityFn = cosineSimilarity) {
    const similarities = vectorDatabase.map((vec, index) => ({
        index,
        similarity: similarityFn(queryVector, vec)
    }));
    // Sort by similarity in descending order
    similarities.sort((a, b) => b.similarity - a.similarity);
    // Return top k results
    return similarities.slice(0, k);
}
/**
 * Cluster vectors using k-means algorithm
 * Returns cluster assignments (0 to k-1) for each vector
 */
function kMeansClustering(vectors, k, maxIterations = 100) {
    if (vectors.length === 0) {
        throw new Error('Cannot cluster empty vector array');
    }
    if (vectors.length < k) {
        throw new Error('Number of vectors must be at least k');
    }
    const vecLength = vectors[0].length;
    // Check that all vectors have the same length
    if (!vectors.every(vec => vec.length === vecLength)) {
        throw new Error('All vectors must have the same length');
    }
    // Initialize centroids randomly by picking random vectors
    const centroids = [];
    const usedIndices = new Set();
    while (centroids.length < k) {
        const randIndex = Math.floor(Math.random() * vectors.length);
        if (!usedIndices.has(randIndex)) {
            usedIndices.add(randIndex);
            centroids.push([...vectors[randIndex]]);
        }
    }
    // Initialize cluster assignments
    let assignments = new Array(vectors.length).fill(0);
    let iterations = 0;
    let changed = true;
    // Main k-means loop
    while (changed && iterations < maxIterations) {
        changed = false;
        iterations++;
        // Assign each vector to nearest centroid
        for (let i = 0; i < vectors.length; i++) {
            const vec = vectors[i];
            let minDist = Infinity;
            let minIndex = 0;
            for (let j = 0; j < centroids.length; j++) {
                const dist = euclideanDistance(vec, centroids[j]);
                if (dist < minDist) {
                    minDist = dist;
                    minIndex = j;
                }
            }
            if (assignments[i] !== minIndex) {
                assignments[i] = minIndex;
                changed = true;
            }
        }
        // Recompute centroids
        const counts = new Array(k).fill(0);
        const newCentroids = Array.from({ length: k }, () => new Array(vecLength).fill(0));
        for (let i = 0; i < vectors.length; i++) {
            const vec = vectors[i];
            const cluster = assignments[i];
            counts[cluster]++;
            for (let j = 0; j < vecLength; j++) {
                newCentroids[cluster][j] += vec[j];
            }
        }
        // Update centroids
        for (let i = 0; i < k; i++) {
            if (counts[i] > 0) {
                for (let j = 0; j < vecLength; j++) {
                    centroids[i][j] = newCentroids[i][j] / counts[i];
                }
            }
        }
    }
    return assignments;
}
exports.default = {
    dotProduct,
    magnitude,
    cosineSimilarity,
    euclideanDistance,
    normalizeVector,
    averageVectors,
    weightedAverageVectors,
    findNearestNeighbors,
    kMeansClustering
};
//# sourceMappingURL=vectorUtils.js.map