{"version":3,"file":"index.node.mjs","names":[],"sources":["../../src/calculator/ElevationFunctions.ts","../../src/calculator/ElevationCalculator.ts","../../src/types.ts","../../src/utils/Logger.ts","../../src/utils/index.ts","../../src/utils/Constants.ts","../../src/utils/Distance.ts","../../src/utils/Vector3D.ts","../../src/utils/EcefConverter.ts","../../src/utils/DouglasPeucker.ts","../../src/utils/ElevationSmoother.ts","../../src/calculator/Reactive.ts","../../src/calculator/BatchCalculator.ts","../../src/tile/cache/ReentrantLock.ts","../../src/tile/cache/Cache.ts","../../src/tile/cache/index.ts","../../src/tile/fetcher/TileLoader.ts","../../src/tile/fetcher/CanvasPool.ts","../../src/tile/fetcher/index.ts","../../src/tile/fetcher/nodejs/NodeJsTile.ts","../../src/tile/fetcher/nodejs/NodeJsTileFetcher.ts","../../src/tile/TileManager.ts","../../src/tile/Tile.ts","../../src/tile/index.ts","../../src/ElevationProvider.ts"],"sourcesContent":["import { Coordinates, Pixel, TileCoordinates, TileCoordinatesFloat } from '../types';\n\n// ========================================================================\n// INTERNAL FUNCTIONS (exported for tests)\n// ========================================================================\n\n/**\n * Convert degrees to radians\n */\nexport function degToRad(degrees: number): number {\n    return (degrees * Math.PI) / 180;\n}\n\n/**\n * Validate latitude is within Web Mercator bounds\n */\nexport function isValidLatitude(lat: number): boolean {\n    return lat >= -85.0511 && lat <= 85.0511;\n}\n\n/**\n * Validate longitude is within valid range\n */\nexport function isValidLongitude(lon: number): boolean {\n    return lon >= -180 && lon <= 180;\n}\n\n/**\n * Validate zoom level is within supported range\n */\nexport function isValidZoomLevel(zoom: number): boolean {\n    return Number.isInteger(zoom) && zoom >= 0 && zoom <= 15;\n}\n\nexport function normalizePixel(pixel: Pixel, tileSize: number): Pixel {\n    let { x, y } = pixel;\n    const tile = pixel.tile;\n    let tileX = tile.x;\n    let tileY = tile.y;\n    const z = tile.z;\n\n    if (x < 0) {\n        x += tileSize;\n        tileX -= 1;\n    }\n    if (x >= tileSize) {\n        x -= tileSize;\n        tileX += 1;\n    }\n    if (y < 0) {\n        y += tileSize;\n        tileY -= 1;\n    }\n    if (y >= tileSize) {\n        y -= tileSize;\n        tileY += 1;\n    }\n\n    const maxTile = Math.pow(2, z) - 1;\n    tileX = Math.max(0, Math.min(maxTile, tileX));\n    tileY = Math.max(0, Math.min(maxTile, tileY));\n\n    return { tile: { z, x: tileX, y: tileY }, x, y };\n}\n\nexport function toTileCoordinatesFloat(coords: Coordinates, z: number): TileCoordinatesFloat {\n    // Input validation\n    if (!isValidLatitude(coords.latitude)) {\n        throw new Error(\n            `Invalid latitude: ${coords.latitude}. Must be between -85.0511 and 85.0511`\n        );\n    }\n\n    if (!isValidLongitude(coords.longitude)) {\n        throw new Error(`Invalid longitude: ${coords.longitude}. Must be between -180 and 180`);\n    }\n\n    if (!isValidZoomLevel(z)) {\n        throw new Error(`Invalid zoom level: ${z}. Must be between 0 and 15`);\n    }\n\n    // Web Mercator projection calculation\n    const lat = degToRad(coords.latitude);\n    const n = Math.pow(2, z);\n    const xFloat = ((coords.longitude + 180) / 360) * n;\n    const yFloat = ((1 - Math.log(Math.tan(lat) + 1 / Math.cos(lat)) / Math.PI) / 2) * n;\n\n    let x = Math.floor(xFloat);\n    let y = Math.floor(yFloat);\n\n    // Clamp tile coordinates to valid range for the zoom level\n    const maxTile = n - 1;\n    x = Math.max(0, Math.min(maxTile, x));\n    y = Math.max(0, Math.min(maxTile, y));\n\n    return {\n        x,\n        y,\n        xFloat,\n        yFloat,\n        z,\n    };\n}\n\nexport function toTileCoordinates(coords: Coordinates, z: number): TileCoordinates {\n    const tile = toTileCoordinatesFloat(coords, z);\n    return {\n        x: tile.x,\n        y: tile.y,\n        z: tile.z,\n    };\n}\n\n/**\n * Convert WGS84 coordinates to Web Mercator tile pixel coordinates\n * @param coords - WGS84 latitude/longitude coordinates\n * @param z - Zoom level (0-15)\n * @returns Pixel coordinates within the appropriate tile\n */\nexport function toPixel(coords: Coordinates, z: number, tileSize: number): Pixel {\n    const tile = toTileCoordinatesFloat(coords, z);\n\n    // Calculate pixel coordinates within the tile\n    const x = Math.floor((tile.xFloat - tile.x) * tileSize);\n    const y = Math.floor((tile.yFloat - tile.y) * tileSize);\n\n    return {\n        tile: {\n            z,\n            x: tile.x,\n            y: tile.y,\n        },\n        x: Math.max(0, Math.min(tileSize - 1, x)),\n        y: Math.max(0, Math.min(tileSize - 1, y)),\n    };\n}\n","import { toPixel, normalizePixel } from './ElevationFunctions';\nimport type { TileManager } from '../tile';\nimport type { Pixel, Coordinates } from '../types';\n\nexport class ElevationCalculator {\n    private readonly tileManager: TileManager;\n    private readonly tileSize: number;\n\n    constructor(tileManager: TileManager, tileSize: number = 256) {\n        this.tileManager = tileManager;\n        this.tileSize = tileSize;\n    }\n\n    // ========================================================================\n    // PUBLIC API - ELEVATION CALCULATIONS\n    // ========================================================================\n\n    public async getElevation(\n        coords: Coordinates,\n        zoomLevel: number,\n        interpolation: boolean = true\n    ): Promise<number> {\n        try {\n            if (interpolation) {\n                return await this.getInterpolatedElevationInternal(coords, zoomLevel);\n            } else {\n                const pixel = toPixel(coords, zoomLevel, this.tileSize);\n                return await this.getElevationFromPixel(pixel);\n            }\n        } catch (error) {\n            if (error instanceof Error) {\n                throw new Error(`Failed to get elevation: ${error.message}`, { cause: error });\n            }\n            throw new Error('Failed to get elevation: Unknown error', { cause: error });\n        }\n    }\n\n    // ========================================================================\n    // PRIVATE - HELPER METHODS\n    // ========================================================================\n\n    private async getInterpolatedElevationInternal(\n        coords: Coordinates,\n        zoomLevel: number\n    ): Promise<number> {\n        const pixel = toPixel(coords, zoomLevel, this.tileSize);\n        const pixelFloat = {\n            tile: pixel.tile,\n            x: pixel.x,\n            y: pixel.y,\n        };\n        const x0 = Math.floor(pixelFloat.x);\n        const y0 = Math.floor(pixelFloat.y);\n        const x1 = x0 + 1;\n        const y1 = y0 + 1;\n\n        const dx = pixelFloat.x - x0;\n        const dy = pixelFloat.y - y0;\n\n        const p00 = await this.getElevationFromPixel(\n            normalizePixel({ tile: pixelFloat.tile, x: x0, y: y0 }, this.tileSize)\n        );\n        const p10 = await this.getElevationFromPixel(\n            normalizePixel({ tile: pixelFloat.tile, x: x1, y: y0 }, this.tileSize)\n        );\n        const p01 = await this.getElevationFromPixel(\n            normalizePixel({ tile: pixelFloat.tile, x: x0, y: y1 }, this.tileSize)\n        );\n        const p11 = await this.getElevationFromPixel(\n            normalizePixel({ tile: pixelFloat.tile, x: x1, y: y1 }, this.tileSize)\n        );\n\n        const top = p00 * (1 - dx) + p10 * dx;\n        const bottom = p01 * (1 - dx) + p11 * dx;\n        return top * (1 - dy) + bottom * dy;\n    }\n\n    /**\n     * Get elevation for a specific pixel (internal helper)\n     */\n    private async getElevationFromPixel(pixel: Pixel): Promise<number> {\n        let cachedTile = this.tileManager.getTileDirect(pixel.tile);\n        if (!cachedTile) {\n            cachedTile = await this.tileManager.getTile(pixel.tile);\n        }\n        return cachedTile.getElevation(pixel);\n    }\n}\n","/**\n * Geographic coordinates in WGS84\n */\nexport interface Coordinates {\n    readonly latitude: number;\n    readonly longitude: number;\n    elevation?: number;\n}\n\nexport interface CoordinatesElevation extends Coordinates {\n    elevation: number;\n}\n\nexport function asCoordinatesElevation(coordinates: Coordinates): CoordinatesElevation {\n    return { ...coordinates, elevation: coordinates.elevation ?? 0 };\n}\n\n/**\n * Tile coordinates in Web Mercator projection\n */\nexport interface TileCoordinates {\n    readonly x: number;\n    readonly y: number;\n    readonly z: number;\n}\n\nexport interface TileCoordinatesFloat {\n    readonly x: number;\n    readonly y: number;\n    readonly xFloat: number;\n    readonly yFloat: number;\n    readonly z: number;\n}\n\nexport interface Pixel {\n    readonly tile: TileCoordinates;\n    readonly x: number;\n    readonly y: number;\n}\n\n/**\n * RGB color values from terrain tile\n */\nexport interface RGBColor {\n    readonly red: number;\n    readonly green: number;\n    readonly blue: number;\n}\n\n/**\n * Configuration options for ElevationProvider\n */\nexport interface ElevationProviderConfig {\n    /**\n     * Tile zoom level (0-15, default: 12)\n     * Higher zoom = better resolution but more tiles\n     */\n    readonly zoomLevel?: number;\n\n    /**\n     * Maximum number of tiles to keep in memory cache\n     * Default: 100\n     */\n    readonly cacheSize?: number;\n\n    /**\n     * Custom tile URL template\n     * Default: AWS S3 Terrarium tiles\n     */\n    readonly tileUrlTemplate?: string;\n\n    /**\n     * Tile size in pixels (default: 256)\n     * Use 512 for providers like mapterhorn.com\n     */\n    readonly tileSize?: number;\n\n    /**\n     * Attribution for the tile data source\n     */\n    readonly attribution?: Attribution;\n}\n\n/**\n * Attribution information for elevation data\n */\nexport interface Attribution {\n    readonly text: string;\n    readonly url?: string;\n}\n\n/**\n * Options for Douglas-Peucker filtering of elevation profiles\n */\nexport interface FilterOptions {\n    /**\n     * Maximum allowed perpendicular distance from simplified line in meters\n     * Default: 10 meters\n     */\n    readonly tolerance?: number;\n\n    /**\n     * Elevation exaggeration factor for ECEF coordinate conversion\n     * Higher values emphasize elevation differences more\n     * Default: 3\n     */\n    readonly zExaggeration?: number;\n\n    /**\n     * Whether filtering is enabled\n     * Default: false\n     */\n    readonly enabled?: boolean;\n}\n\n/**\n * Options for distance-based elevation smoothing\n */\nexport interface SmoothingOptions {\n    /**\n     * Smoothing window size in meters\n     * Points within this distance will be weighted and averaged\n     * Default: 50 meters\n     */\n    readonly windowSize?: number;\n\n    /**\n     * Whether smoothing is enabled\n     * Default: false\n     */\n    readonly enabled?: boolean;\n}\n\n/**\n * 3D vector in ECEF (Earth-Centered, Earth-Fixed) coordinates\n */\nexport interface Vector3D {\n    readonly x: number;\n    readonly y: number;\n    readonly z: number;\n}\n\n/**\n * Options for getElevation method\n */\nexport interface GetElevationOptions {\n    /**\n     * Use bilinear interpolation for smoother results\n     * Default: true\n     */\n    readonly interpolation?: boolean;\n}\n\n/**\n * Options for setElevations method\n */\nexport interface SetElevationsOptions {\n    /**\n     * Use bilinear interpolation for smoother results\n     * Default: true\n     */\n    readonly interpolation?: boolean;\n}\n\n/**\n * Options for getElevationsAlong method\n */\nexport interface GetElevationsAlongOptions {\n    /**\n     * Distance between elevation points in meters\n     * Default: 10\n     */\n    readonly step?: number;\n\n    readonly minDistance?: number;\n\n    /**\n     * Use bilinear interpolation for smoother results\n     * Default: true\n     */\n    readonly interpolation?: boolean;\n\n    /**\n     * Optional distance-based smoothing options\n     */\n    readonly smoothingOptions?: SmoothingOptions;\n\n    /**\n     * Optional Douglas-Peucker filtering options\n     */\n    readonly filterOptions?: FilterOptions;\n}\n","/**\n * Lightweight logger for development debugging\n * All logging is completely removed in production builds via __DEV__ constant\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ntype LogFunction = (message?: any, ...optionalParams: any[]) => void;\n\ntype LogLevelConfig = 'error' | 'warn' | 'info' | 'debug' | 'trace';\ntype LevelsConfig = {\n    default: LogLevelConfig;\n    [namespacePrefix: string]: LogLevelConfig;\n};\nimport jsonLevels from './levels.json' with { type: 'json' };\nconst levels = jsonLevels as LevelsConfig;\n\nexport type LogLevelValue = 0 | 1 | 2 | 3 | 4;\nexport class LogLevel {\n    static readonly ERROR: LogLevelValue = 0;\n    static readonly WARN: LogLevelValue = 1;\n    static readonly INFO: LogLevelValue = 2;\n    static readonly DEBUG: LogLevelValue = 3;\n    static readonly TRACE: LogLevelValue = 4;\n}\n\nconst levelWeights: Record<LogLevelConfig, LogLevelValue> = {\n    error: LogLevel.ERROR,\n    warn: LogLevel.WARN,\n    info: LogLevel.INFO,\n    debug: LogLevel.DEBUG,\n    trace: LogLevel.TRACE,\n};\n\nconst levelLabel: Record<LogLevelValue, string> = {\n    0: 'ERROR',\n    1: 'WARN',\n    2: 'INFO',\n    3: 'DEBUG',\n    4: 'TRACE',\n};\n\nconst levelLog: Record<LogLevelValue, LogFunction> = {\n    0: console.error,\n    1: console.error,\n    2: console.log,\n    3: console.log,\n    4: console.log,\n};\n\n/**\n * Logger class that provides conditional logging based on build environment\n * In production builds, all logging code is eliminated by the bundler\n *\n * Matches the full console API signatures for compatibility with printf-style formatting\n * and all console features\n */\nclass Logger {\n    private namespace: string;\n    private level: LogLevelValue;\n\n    constructor(namespace: string) {\n        this.namespace = namespace;\n        if (__DEV__) {\n            let matchedLevel: LogLevelConfig = levels.default;\n            let longestMatch = 0;\n\n            for (const [prefix, level] of Object.entries(levels)) {\n                if (\n                    prefix !== 'default' &&\n                    namespace.startsWith(prefix) &&\n                    prefix.length > longestMatch\n                ) {\n                    matchedLevel = level;\n                    longestMatch = prefix.length;\n                }\n            }\n\n            this.level = levelWeights[matchedLevel];\n        } else {\n            this.level = levelWeights.warn;\n        }\n    }\n\n    private shouldLog(level: LogLevelValue): boolean {\n        return level <= this.level;\n    }\n\n    private doLog(level: LogLevelValue, message?: any, ...optionalParams: any[]) {\n        const prefix = `[${this.namespace}:${levelLabel[level]}]`;\n        if (typeof message === 'string') {\n            // Preserve printf-style formatting by combining prefix with format string\n            levelLog[level](`${prefix} ${message}`, ...optionalParams);\n        } else {\n            // No formatting needed, use regular logging\n            levelLog[level](prefix, message, ...optionalParams);\n        }\n    }\n\n    private log(level: LogLevelValue, message?: any, ...optionalParams: any[]) {\n        if (this.shouldLog(level)) {\n            this.doLog(level, message, ...optionalParams);\n        }\n    }\n\n    /**\n     * Log debug information (verbose output for development)\n     * Supports printf-style formatting: logger.debug('Value: %s, Count: %d', value, count)\n     */\n    trace(message?: any, ...optionalParams: any[]): void {\n        if (__DEV__) {\n            this.log(LogLevel.TRACE, message, ...optionalParams);\n        }\n    }\n\n    /**\n     * Log debug information (verbose output for development)\n     * Supports printf-style formatting: logger.debug('Value: %s, Count: %d', value, count)\n     */\n    debug(message?: any, ...optionalParams: any[]): void {\n        if (__DEV__) {\n            this.log(LogLevel.DEBUG, message, ...optionalParams);\n        }\n    }\n\n    /**\n     * Log general information\n     * Supports printf-style formatting: logger.info('User %s logged in', username)\n     */\n    info(message?: any, ...optionalParams: any[]): void {\n        if (__DEV__) {\n            this.log(LogLevel.INFO, message, ...optionalParams);\n        }\n    }\n\n    /**\n     * Log warnings\n     * Supports printf-style formatting: logger.warn('Timeout after %dms', timeout)\n     */\n    warn(message?: any, ...optionalParams: any[]): void {\n        this.log(LogLevel.WARN, message, ...optionalParams);\n    }\n\n    /**\n     * Log errors\n     * Supports printf-style formatting: logger.error('Failed to load %s: %o', file, error)\n     */\n    error(message?: any, ...optionalParams: any[]): void {\n        this.log(LogLevel.ERROR, message, ...optionalParams);\n    }\n\n    private getTimeLabel(level: LogLevelValue, label: string): string {\n        return `[${this.namespace}:${levelLabel[level]}] ${label}`;\n    }\n\n    private doTime(level: LogLevelValue, label: string): void {\n        console.time(this.getTimeLabel(level, label));\n    }\n\n    private doTimeEnd(level: LogLevelValue, label: string): void {\n        console.timeEnd(this.getTimeLabel(level, label));\n    }\n\n    /**\n     * Log with timing information\n     * Useful for performance debugging\n     */\n    timeLevel(level: LogLevelValue, label: string): void {\n        if (__DEV__) {\n            if (this.shouldLog(level)) {\n                this.doTime(level, label);\n            }\n        }\n    }\n\n    /**\n     * End timing and log duration\n     */\n    timeEndLevel(level: LogLevelValue, label: string): void {\n        if (__DEV__) {\n            if (this.shouldLog(level)) {\n                this.doTimeEnd(level, label);\n            }\n        }\n    }\n\n    /**\n     * Log with timing information\n     * Useful for performance debugging\n     */\n    time(label: string): void {\n        this.doTime(LogLevel.INFO, label);\n    }\n\n    /**\n     * End timing and log duration\n     */\n    timeEnd(label: string): void {\n        this.doTimeEnd(LogLevel.INFO, label);\n    }\n\n    private logDir(level: LogLevelValue, message?: any, obj?: any, options?: any) {\n        this.doLog(level, 'DIR %s', message);\n        console.dir(obj, options);\n    }\n\n    /**\n     * Display an interactive list of object properties\n     * Useful for exploring complex objects in development\n     * @param obj - The object to inspect\n     * @param options - Optional display options\n     */\n    dirLevel(level: LogLevelValue, message?: any, obj?: any, options?: any): void {\n        if (__DEV__) {\n            if (this.shouldLog(level)) {\n                this.logDir(level, message, obj, options);\n            }\n        }\n    }\n\n    /**\n     * Display an interactive list of object properties\n     * Useful for exploring complex objects in development\n     * @param obj - The object to inspect\n     * @param options - Optional display options\n     */\n    dir(message?: any, obj?: any, options?: any): void {\n        this.logDir(LogLevel.INFO, message, obj, options);\n    }\n\n    /**\n     * Clear the console\n     */\n    clear(): void {\n        console.clear();\n    }\n}\n\n/**\n * Create a logger instance with a specific namespace\n * @param namespace - The namespace for this logger (e.g., 'Cache', 'TileFetcher')\n * @returns A new Logger instance with the specified namespace\n * @example\n * const logger = createLogger('MyModule');\n * logger.debug('Module initialized');\n */\nexport const createLogger = (namespace: string): Logger => new Logger(namespace);\n\nexport { Logger };\n","export { createLogger, Logger, LogLevel } from './Logger';\n","/**\n * Earth and mathematical constants used throughout the elevation library\n */\nexport const EARTH_CONSTANTS = {\n    /** Semi-major axis in meters (WGS84 ellipsoid) */\n    SEMI_MAJOR_AXIS: 6378137.0,\n    /** Mean radius in meters (used for distance calculations) */\n    MEAN_RADIUS: 6371000,\n    /** First eccentricity squared (WGS84 ellipsoid) */\n    FIRST_ECCENTRICITY_SQUARED: 0.00669437999014,\n} as const;\n\n/**\n * Mathematical constants\n */\nexport const MATH_CONSTANTS = {\n    /** Degrees to radians conversion factor */\n    DEG_TO_RAD: Math.PI / 180,\n    /** Radians to degrees conversion factor */\n    RAD_TO_DEG: 180 / Math.PI,\n} as const;\n\n/**\n * Algorithm constants\n */\nexport const ALGORITHM_CONSTANTS = {\n    /** Minimum points needed for smoothing operations */\n    MIN_SMOOTHING_POINTS: 3,\n} as const;\n","import { Coordinates } from '../types';\nimport { EARTH_CONSTANTS, MATH_CONSTANTS } from './Constants';\nimport { Vector3D } from './Vector3D';\n\n/**\n * Distance calculation utilities for geographic and 3D coordinates\n */\nexport class Distance {\n    /**\n     * Calculate great circle distance between two geographic coordinates using Haversine formula\n     * @param coord1 - First coordinate\n     * @param coord2 - Second coordinate\n     * @returns Distance in meters\n     */\n    public static haversine(coord1: Coordinates, coord2: Coordinates): number {\n        const lat1Rad = coord1.latitude * MATH_CONSTANTS.DEG_TO_RAD;\n        const lat2Rad = coord2.latitude * MATH_CONSTANTS.DEG_TO_RAD;\n        const deltaLat = (coord2.latitude - coord1.latitude) * MATH_CONSTANTS.DEG_TO_RAD;\n        const deltaLon = (coord2.longitude - coord1.longitude) * MATH_CONSTANTS.DEG_TO_RAD;\n\n        const a =\n            Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +\n            Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);\n\n        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n        return EARTH_CONSTANTS.MEAN_RADIUS * c;\n    }\n\n    /**\n     * Calculate Euclidean distance between two 3D points\n     * @param point1 - First 3D point\n     * @param point2 - Second 3D point\n     * @returns Distance in meters\n     */\n    public static euclidean3D(point1: Vector3D, point2: Vector3D): number {\n        const dx = point1.x - point2.x;\n        const dy = point1.y - point2.y;\n        const dz = point1.z - point2.z;\n        return Math.sqrt(dx * dx + dy * dy + dz * dz);\n    }\n\n    /**\n     * Calculate perpendicular distance from a point to a line segment in 3D space\n     * @param point - Point to measure from\n     * @param segmentStart - Start point of line segment\n     * @param segmentEnd - End point of line segment\n     * @returns Perpendicular distance in meters\n     */\n    public static pointToSegment3D(\n        point: Vector3D,\n        segmentStart: Vector3D,\n        segmentEnd: Vector3D\n    ): number {\n        const segmentVector = segmentEnd.subtract(segmentStart);\n        const pointVector = point.subtract(segmentStart);\n\n        // Check if segment has zero length\n        const segmentLengthSquared = segmentVector.dot(segmentVector);\n        if (segmentLengthSquared === 0) {\n            return Distance.euclidean3D(point, segmentStart);\n        }\n\n        // Calculate parameter t for closest point on segment\n        const t = Math.max(0, Math.min(1, pointVector.dot(segmentVector) / segmentLengthSquared));\n\n        // Calculate closest point on segment\n        const closestPoint = segmentStart.add(segmentVector.multiply(t));\n\n        // Return distance to closest point\n        return Distance.euclidean3D(point, closestPoint);\n    }\n\n    /**\n     * Calculate cumulative distances along a path of coordinates\n     * @param points - Array of coordinates\n     * @returns Array of cumulative distances in meters\n     */\n    public static cumulativeDistances(points: Coordinates[]): number[] {\n        const distances: number[] = [0];\n\n        for (let i = 1; i < points.length; i++) {\n            const segmentDistance = Distance.haversine(points[i - 1], points[i]);\n            distances.push(distances[i - 1] + segmentDistance);\n        }\n\n        return distances;\n    }\n\n    /**\n     * Calculate total distance along a path of coordinates\n     * @param points - Array of coordinates\n     * @returns Total distance in meters\n     */\n    public static totalPathDistance(points: Coordinates[]): number {\n        if (points.length < 2) {\n            return 0;\n        }\n\n        let totalDistance = 0;\n        for (let i = 1; i < points.length; i++) {\n            totalDistance += Distance.haversine(points[i - 1], points[i]);\n        }\n\n        return totalDistance;\n    }\n}\n","/**\n * 3D Vector class for ECEF coordinate operations\n */\nexport class Vector3D {\n    constructor(\n        public readonly x: number,\n        public readonly y: number,\n        public readonly z: number\n    ) {}\n\n    /**\n     * Calculate Euclidean distance between two vectors\n     */\n    public distanceTo(other: Vector3D): number {\n        const dx = this.x - other.x;\n        const dy = this.y - other.y;\n        const dz = this.z - other.z;\n        return Math.hypot(dx, dy, dz);\n    }\n\n    /**\n     * Subtract two vectors\n     */\n    public subtract(other: Vector3D): Vector3D {\n        return new Vector3D(this.x - other.x, this.y - other.y, this.z - other.z);\n    }\n\n    /**\n     * Add two vectors\n     */\n    public add(other: Vector3D): Vector3D {\n        return new Vector3D(this.x + other.x, this.y + other.y, this.z + other.z);\n    }\n\n    /**\n     * Multiply vector by scalar\n     */\n    public multiply(scalar: number): Vector3D {\n        return new Vector3D(this.x * scalar, this.y * scalar, this.z * scalar);\n    }\n\n    /**\n     * Calculate dot product with another vector\n     */\n    public dot(other: Vector3D): number {\n        return this.x * other.x + this.y * other.y + this.z * other.z;\n    }\n\n    /**\n     * Calculate cross product with another vector\n     */\n    public cross(other: Vector3D): Vector3D {\n        return new Vector3D(\n            this.y * other.z - this.z * other.y,\n            this.z * other.x - this.x * other.z,\n            this.x * other.y - this.y * other.x\n        );\n    }\n\n    /**\n     * Calculate the magnitude (length) of the vector\n     */\n    public magnitude(): number {\n        return Math.hypot(this.x, this.y, this.z);\n    }\n\n    /**\n     * Normalize the vector to unit length\n     */\n    public normalize(): Vector3D {\n        const mag = this.magnitude();\n        if (mag === 0) {\n            return new Vector3D(0, 0, 0);\n        }\n        return this.multiply(1 / mag);\n    }\n\n    /**\n     * Calculate the shortest distance from this point to a line segment\n     */\n    public distanceToSegment(segmentStart: Vector3D, segmentEnd: Vector3D): number {\n        const segmentVector = segmentEnd.subtract(segmentStart);\n        const segmentLength = segmentVector.magnitude();\n\n        // Handle degenerate case where segment has zero length\n        if (segmentLength === 0) {\n            return this.distanceTo(segmentStart);\n        }\n\n        // Vector from segment start to this point\n        const pointVector = this.subtract(segmentStart);\n\n        // Project point vector onto segment vector\n        const projection = pointVector.dot(segmentVector) / (segmentLength * segmentLength);\n\n        // Clamp projection to segment bounds [0, 1]\n        const clampedProjection = Math.max(0, Math.min(1, projection));\n\n        // Find closest point on segment\n        const closestPoint = segmentStart.add(segmentVector.multiply(clampedProjection));\n\n        // Return distance to closest point\n        return this.distanceTo(closestPoint);\n    }\n}\n","import { Coordinates } from '../types';\nimport { EARTH_CONSTANTS } from './Constants';\nimport { Vector3D } from './Vector3D';\n\n/**\n * ECEF (Earth-Centered, Earth-Fixed) coordinate converter\n * Converts WGS84 coordinates (lat/lon/elevation) to ECEF Cartesian coordinates\n */\nexport class EcefConverter {\n    /**\n     * Convert WGS84 coordinates to ECEF coordinates with optional elevation exaggeration\n     * @param coordinates - Geographic coordinates with elevation\n     * @param zExaggeration - Elevation exaggeration factor (default: 3)\n     * @returns ECEF coordinates as Vector3D\n     */\n    public static toEcef(coordinates: Coordinates, zExaggeration: number = 3): Vector3D {\n        // Convert degrees to radians\n        const latRad = (coordinates.latitude * Math.PI) / 180;\n        const lonRad = (coordinates.longitude * Math.PI) / 180;\n\n        // Apply elevation exaggeration\n        const elevationExaggerated = zExaggeration * (coordinates.elevation || 0);\n\n        // Calculate prime vertical radius of curvature\n        const sinLat = Math.sin(latRad);\n        const n =\n            EARTH_CONSTANTS.SEMI_MAJOR_AXIS /\n            Math.sqrt(1 - EARTH_CONSTANTS.FIRST_ECCENTRICITY_SQUARED * sinLat * sinLat);\n\n        // Calculate ECEF coordinates\n        const cosLat = Math.cos(latRad);\n        const cosLon = Math.cos(lonRad);\n        const sinLon = Math.sin(lonRad);\n\n        const x = (n + elevationExaggerated) * cosLat * cosLon;\n        const y = (n + elevationExaggerated) * cosLat * sinLon;\n        const z =\n            (n * (1 - EARTH_CONSTANTS.FIRST_ECCENTRICITY_SQUARED) + elevationExaggerated) * sinLat;\n\n        return new Vector3D(x, y, z);\n    }\n\n    /**\n     * Convert multiple coordinates to ECEF vectors\n     * @param coordinates - Array of geographic coordinates with elevation\n     * @param zExaggeration - Elevation exaggeration factor (default: 3)\n     * @returns Array of ECEF coordinates as Vector3D\n     */\n    public static convertBatch(coordinates: Coordinates[], zExaggeration: number = 3): Vector3D[] {\n        return coordinates.map(coord => this.toEcef(coord, zExaggeration));\n    }\n}\n","import { CoordinatesElevation } from '../types';\nimport { EcefConverter } from './EcefConverter';\nimport { createLogger, Logger, LogLevel } from './Logger';\n\nconst logger: Logger = createLogger('utils/DouglasPeucker');\n\n/**\n * 3D Douglas-Peucker algorithm implementation for elevation profile simplification\n * Uses ECEF coordinates for true 3D distance calculations\n */\nexport class DouglasPeucker {\n    /**\n     * Simplify a path using the Douglas-Peucker algorithm in 3D space\n     * @param points - Array of coordinates with elevation\n     * @param tolerance - Maximum allowed distance from simplified line in meters\n     * @param zExaggeration - Elevation exaggeration factor for ECEF conversion (default: 3)\n     * @returns Simplified array of coordinates\n     */\n    public static simplify(\n        points: CoordinatesElevation[],\n        tolerance: number,\n        zExaggeration: number = 3\n    ): CoordinatesElevation[] {\n        logger.info('simplify %s', points.length);\n        if (points.length <= 2) {\n            logger.warn('too small');\n            return [...points]; // Return copy to avoid mutation\n        }\n        logger.timeLevel(LogLevel.INFO, 'simplify');\n\n        const lastIndex = points.length - 1;\n        const simplified: CoordinatesElevation[] = [];\n\n        // Always include first point\n        simplified.push(points[0]);\n\n        // Recursively simplify the path\n        const intermediatePoints = this.simplifyRecursive(\n            points,\n            0,\n            lastIndex,\n            tolerance,\n            zExaggeration\n        );\n        simplified.push(...intermediatePoints);\n\n        // Always include last point\n        simplified.push(points[lastIndex]);\n\n        logger.timeEndLevel(LogLevel.INFO, 'simplify');\n        logger.debug('simplified -> %s', simplified.length);\n        return simplified;\n    }\n\n    /**\n     * Recursive step of the Douglas-Peucker algorithm\n     * @param points - Array of all points\n     * @param firstIndex - Index of first point in current segment\n     * @param lastIndex - Index of last point in current segment\n     * @param tolerance - Maximum allowed distance in meters\n     * @param zExaggeration - Elevation exaggeration factor\n     * @returns Array of points to include in simplified path\n     */\n    private static simplifyRecursive(\n        points: CoordinatesElevation[],\n        firstIndex: number,\n        lastIndex: number,\n        tolerance: number,\n        zExaggeration: number\n    ): CoordinatesElevation[] {\n        let maxDistance = 0;\n        let maxIndex = -1;\n        const result: CoordinatesElevation[] = [];\n\n        // Convert segment endpoints to ECEF\n        const firstEcef = EcefConverter.toEcef(points[firstIndex], zExaggeration);\n        const lastEcef = EcefConverter.toEcef(points[lastIndex], zExaggeration);\n\n        // Find the point with maximum perpendicular distance to the line segment\n        for (let i = firstIndex + 1; i < lastIndex; i++) {\n            const pointEcef = EcefConverter.toEcef(points[i], zExaggeration);\n            const distance = pointEcef.distanceToSegment(firstEcef, lastEcef);\n\n            if (distance > maxDistance) {\n                maxDistance = distance;\n                maxIndex = i;\n            }\n        }\n\n        // If the maximum distance is greater than tolerance, split the segment\n        if (maxDistance > tolerance && maxIndex !== -1) {\n            // Recursively simplify the first sub-segment\n            if (maxIndex - firstIndex > 1) {\n                const leftSegment = this.simplifyRecursive(\n                    points,\n                    firstIndex,\n                    maxIndex,\n                    tolerance,\n                    zExaggeration\n                );\n                result.push(...leftSegment);\n            }\n\n            // Include the point with maximum distance\n            result.push(points[maxIndex]);\n\n            // Recursively simplify the second sub-segment\n            if (lastIndex - maxIndex > 1) {\n                const rightSegment = this.simplifyRecursive(\n                    points,\n                    maxIndex,\n                    lastIndex,\n                    tolerance,\n                    zExaggeration\n                );\n                result.push(...rightSegment);\n            }\n        }\n\n        return result;\n    }\n}\n","import { CoordinatesElevation } from '../types';\nimport { ALGORITHM_CONSTANTS } from './Constants';\nimport { Distance } from './Distance';\nimport { createLogger, Logger, LogLevel } from './Logger';\n\nconst logger: Logger = createLogger('utils/ElevationSmoother');\n\n/**\n * Distance-based elevation smoothing using weighted triangular kernel\n * Based on the algorithm from Java SmoothService\n */\nexport class ElevationSmoother {\n    /**\n     * Apply distance-based smoothing to elevation data\n     * @param points - Array of coordinates with elevation\n     * @param windowSize - Smoothing window in meters (default: 50)\n     * @returns Smoothed elevation data\n     */\n    public static smooth(\n        points: CoordinatesElevation[],\n        windowSize: number = 50\n    ): CoordinatesElevation[] {\n        logger.debug('smooth %s', points.length);\n        // Validate inputs\n        if (points.length < ALGORITHM_CONSTANTS.MIN_SMOOTHING_POINTS) {\n            logger.debug('too small');\n            return points; // Not enough points to smooth\n        }\n\n        if (windowSize <= 0) {\n            throw new Error(`Invalid window size: ${windowSize}. Must be positive`);\n        }\n        logger.timeLevel(LogLevel.INFO, 'smooth');\n\n        // Calculate cumulative distances for efficient range queries\n        const distances = Distance.cumulativeDistances(points);\n\n        // Apply smoothing to each point\n        const smoothedPoints: CoordinatesElevation[] = [];\n\n        for (let i = 0; i < points.length; i++) {\n            const smoothedElevation = this.computeSmoothedValue(i, points, distances, windowSize);\n\n            smoothedPoints.push({\n                ...points[i],\n                elevation: smoothedElevation,\n            });\n        }\n        logger.timeEndLevel(LogLevel.INFO, 'smooth');\n\n        return smoothedPoints;\n    }\n\n    /**\n     * Compute smoothed elevation value for a single point\n     * @param index - Index of point to smooth\n     * @param points - All points\n     * @param distances - Cumulative distances\n     * @param windowSize - Smoothing window in meters\n     * @returns Smoothed elevation value\n     */\n    private static computeSmoothedValue(\n        index: number,\n        points: CoordinatesElevation[],\n        distances: number[],\n        windowSize: number\n    ): number {\n        const currentDistance = distances[index];\n\n        // Find range of points within the window\n        let startIndex = index;\n        while (startIndex > 0 && currentDistance - distances[startIndex - 1] <= windowSize) {\n            startIndex--;\n        }\n\n        let endIndex = index;\n        while (\n            endIndex < points.length - 1 &&\n            distances[endIndex + 1] - currentDistance <= windowSize\n        ) {\n            endIndex++;\n        }\n\n        // Apply weighted averaging using triangular kernel\n        let totalWeight = 0;\n        let weightedSum = 0;\n\n        for (let j = startIndex; j <= endIndex; j++) {\n            const distanceFromCurrent = Math.abs(distances[j] - currentDistance);\n\n            // Triangular kernel: weight = 1 - (distance / windowSize)\n            const weight = 1 - distanceFromCurrent / windowSize;\n\n            totalWeight += weight;\n            weightedSum += points[j].elevation * weight;\n        }\n\n        // Return weighted average, or original value if no valid weights\n        // Note: totalWeight should always be > 0 since current point has weight = 1,\n        // but this check provides defensive programming against edge cases\n        return totalWeight > 0 ? weightedSum / totalWeight : points[index].elevation;\n    }\n}\n","// reactive.ts\nexport type AsyncConsumer<T> = (item: T) => Promise<void>;\n\ntype Inflight = Promise<{ ok: true } | { ok: false; error: unknown }>;\n\nexport class Flux {\n    private static async wrap(p: Promise<void>): Inflight {\n        try {\n            await p;\n            return { ok: true as const };\n        } catch (e) {\n            return { ok: false as const, error: e };\n        }\n    }\n\n    private static async getFirst(inflight: Inflight[]): Promise<void> {\n        const { idx, res } = await Promise.race(\n            inflight.map((it, idx) => it.then(res => ({ idx, res })))\n        );\n        inflight.splice(idx, 1);\n        if (res.ok) {\n            return;\n        } else {\n            throw res.error;\n        }\n    }\n\n    static async forEach<T>(\n        from: Iterable<T>,\n        fn: AsyncConsumer<T>,\n        maxParallel = 1\n    ): Promise<void> {\n        const inflight: Inflight[] = [];\n\n        for (const item of from) {\n            inflight.push(Flux.wrap(fn(item)));\n\n            if (inflight.length >= maxParallel) {\n                await Flux.getFirst(inflight);\n            }\n        }\n\n        while (inflight.length > 0) {\n            await Flux.getFirst(inflight);\n        }\n        return;\n    }\n}\n","import {\n    asCoordinatesElevation,\n    Coordinates,\n    CoordinatesElevation,\n    FilterOptions,\n    SmoothingOptions,\n    TileCoordinates,\n} from '../types';\nimport { createLogger, Logger, LogLevel } from '../utils';\nimport { Distance } from '../utils/Distance';\nimport { DouglasPeucker } from '../utils/DouglasPeucker';\nimport { ElevationSmoother } from '../utils/ElevationSmoother';\nimport { ElevationCalculator } from './ElevationCalculator';\nimport { toTileCoordinates } from './ElevationFunctions';\nimport { Flux } from './Reactive';\n\nconst logger: Logger = createLogger('calculator/BatchCalculator');\n\nexport class BatchCalculator {\n    private readonly elevationCalculator: ElevationCalculator;\n\n    constructor(elevationCalculator: ElevationCalculator) {\n        this.elevationCalculator = elevationCalculator;\n    }\n\n    public async setElevations(\n        coordinates: Iterable<Coordinates>,\n        zoomLevel: number,\n        interpolation: boolean\n    ): Promise<void> {\n        logger.debug('setElevations');\n        const pointsPerTile: Record<string, Coordinates[]> = {};\n        const tileCoordinatesMap: Map<string, TileCoordinates> = new Map();\n\n        logger.timeLevel(LogLevel.DEBUG, 'points-per-tile');\n        // Helper function to create a unique key for tile coordinates\n        const tileKey = (tile: TileCoordinates): string => `${tile.z}/${tile.x}/${tile.y}`;\n\n        // Populate pointsPerTile by grouping coordinates by their tile\n        for (const point of coordinates) {\n            const tile = toTileCoordinates(point, zoomLevel);\n            const key = tileKey(tile);\n\n            let array = pointsPerTile[key];\n            if (!array) {\n                array = [];\n                pointsPerTile[key] = array;\n                tileCoordinatesMap.set(key, tile);\n            }\n            array.push(point);\n        }\n        const tiles = Array.from(tileCoordinatesMap.values());\n        logger.timeEndLevel(LogLevel.DEBUG, 'points-per-tile');\n\n        logger.timeLevel(LogLevel.DEBUG, 'get-elevations');\n        await Flux.forEach(\n            tiles,\n            async tile => {\n                const key = tileKey(tile);\n                logger.timeLevel(LogLevel.DEBUG, 'get-elevations-' + key);\n                const points: Coordinates[] = pointsPerTile[key];\n                for (const point of points) {\n                    point.elevation = await this.elevationCalculator.getElevation(\n                        point,\n                        zoomLevel,\n                        interpolation\n                    );\n                }\n                logger.timeEndLevel(LogLevel.DEBUG, 'get-elevations-' + key);\n            },\n            10\n        );\n        logger.timeEndLevel(LogLevel.DEBUG, 'get-elevations');\n    }\n\n    /**\n     * Get elevations along a path defined by multiple coordinates\n     * @param path - Array of coordinates defining the path\n     * @param zoomLevel - Tile zoom level (0-15)\n     * @param step - Distance between elevation points in meters\n     * @param interpolation - Use bilinear interpolation for smoother results\n     * @param smoothingOptions - Optional distance-based smoothing options\n     * @param filterOptions - Optional filtering options using Douglas-Peucker algorithm\n     */\n    public async getElevationsAlong(\n        path: Coordinates[],\n        zoomLevel: number,\n        step: number,\n        minDistance: number,\n        interpolation: boolean,\n        smoothingOptions?: SmoothingOptions,\n        filterOptions?: FilterOptions\n    ): Promise<CoordinatesElevation[]> {\n        const pathTimer = 'path-elevations';\n        logger.timeLevel(LogLevel.INFO, pathTimer);\n\n        logger.info(\n            'Path processing started - waypoints: %d, step: %dm, zoom: %d, interpolation: %s',\n            path.length,\n            step,\n            zoomLevel,\n            interpolation\n        );\n\n        // Validate inputs\n        if (path.length < 2) {\n            logger.error('Path validation failed - insufficient waypoints: %d', path.length);\n            throw new Error('Path must contain at least 2 coordinates');\n        }\n        if (step <= 1) {\n            logger.error('Path validation failed - step too small: %dm', step);\n            throw new Error(`Step is too small: ${step} meters`);\n        }\n\n        // Generate coordinates along the entire path\n        let coordinates = Array.from(this.generateCoordinatesAlong(path, step, minDistance));\n        logger.debug('Generated %d coordinates along path', coordinates.length);\n\n        // Get elevations for all coordinates\n        logger.debug('Fetching elevations for generated coordinates');\n        await this.setElevations(coordinates, zoomLevel, interpolation);\n\n        logger.debug('Combined coordinates with elevations - points: %d', coordinates.length);\n\n        // Apply smoothing if explicitly enabled\n        if (smoothingOptions?.enabled === true && coordinates.length >= 3) {\n            const windowSize = smoothingOptions.windowSize ?? 50;\n            const originalCount = coordinates.length;\n\n            logger.debug('Applying elevation smoothing - windowSize: %dm', windowSize);\n            const smoothTimer = 'smoothing';\n            logger.timeLevel(LogLevel.DEBUG, smoothTimer);\n\n            coordinates = ElevationSmoother.smooth(coordinates, windowSize);\n\n            logger.timeEndLevel(LogLevel.DEBUG, smoothTimer);\n            logger.debug(\n                'Smoothing completed - points: %d → %d',\n                originalCount,\n                coordinates.length\n            );\n        } else if (smoothingOptions?.enabled === true) {\n            logger.debug(\n                'Smoothing skipped - insufficient points: %d (minimum: 3)',\n                coordinates.length\n            );\n        }\n\n        // Apply filtering if explicitly enabled\n        if (filterOptions?.enabled === true && coordinates.length > 2) {\n            const tolerance = filterOptions?.tolerance ?? 10;\n            const zExaggeration = filterOptions?.zExaggeration ?? 3;\n            const originalCount = coordinates.length;\n\n            logger.debug(\n                'Applying Douglas-Peucker filtering - tolerance: %d, zExaggeration: %d',\n                tolerance,\n                zExaggeration\n            );\n            const filterTimer = 'filtering';\n            logger.timeLevel(LogLevel.DEBUG, filterTimer);\n\n            const filtered = DouglasPeucker.simplify(coordinates, tolerance, zExaggeration);\n\n            logger.timeEndLevel(LogLevel.DEBUG, filterTimer);\n            logger.debug(\n                'Filtering completed - points: %d → %d (%f % reduction)',\n                originalCount,\n                filtered.length,\n                (((originalCount - filtered.length) / originalCount) * 100).toFixed(1)\n            );\n\n            logger.timeEndLevel(LogLevel.INFO, pathTimer);\n            logger.info(\n                'Path processing completed - waypoints: %d, final points: %d, smoothed: %s, filtered: %s',\n                path.length,\n                filtered.length,\n                smoothingOptions?.enabled,\n                filterOptions?.enabled\n            );\n\n            return filtered;\n        } else if (filterOptions?.enabled === true) {\n            logger.debug(\n                'Filtering skipped - insufficient points: %d (minimum: 3)',\n                coordinates.length\n            );\n        }\n\n        logger.timeEndLevel(LogLevel.INFO, pathTimer);\n        logger.info(\n            'Path processing completed - waypoints: %d, final points: %d, smoothed: %s, filtered: %s',\n            path.length,\n            coordinates.length,\n            smoothingOptions?.enabled,\n            filterOptions?.enabled\n        );\n\n        return coordinates;\n    }\n\n    /**\n     * Generate coordinates along a path with multiple waypoints\n     * @param path - Array of coordinates defining the path\n     * @param step - Distance between points in meters\n     */\n    private *generateCoordinatesAlong(\n        path: Coordinates[],\n        step: number,\n        minDistance: number\n    ): Generator<CoordinatesElevation, void, unknown> {\n        if (path.length === 0) {\n            logger.debug('Path generation skipped - insufficient waypoints: %d', path.length);\n            return;\n        }\n        logger.debug('Generating coordinates - waypoints: %d, step: %dm', path.length, step);\n\n        const coordGenTimer = 'coordinate-generation';\n        logger.timeLevel(LogLevel.DEBUG, coordGenTimer);\n\n        // Yield the first point\n        yield asCoordinatesElevation(path[0]);\n        let totalGenerated = 1;\n        let skippedSegments = 0;\n\n        for (let i = 0; i < path.length - 1; i++) {\n            const segmentDistance = Distance.haversine(path[i], path[i + 1]);\n\n            // Skip very short segments (< 1 meter)\n            if (segmentDistance < minDistance) {\n                skippedSegments++;\n                logger.debug(\n                    'Segment %d skipped - distance too short: %.2fm (minimum: %.2fm)',\n                    i + 1,\n                    segmentDistance,\n                    minDistance\n                );\n                continue;\n            }\n\n            // Generate intermediate points for this segment\n            // Skip the first point (already yielded from previous segment)\n            let isFirst = true;\n            for (const coord of this.generateCoordinatesBetween(path[i], path[i + 1], step)) {\n                if (isFirst) {\n                    isFirst = false;\n                    continue; // Skip the first point to avoid duplicates\n                }\n                yield coord;\n                totalGenerated++;\n            }\n        }\n        logger.timeEndLevel(LogLevel.DEBUG, coordGenTimer);\n\n        if (skippedSegments > 0) {\n            logger.debug(\n                'Path generation completed - generated: %d points, skipped segments: %d',\n                totalGenerated,\n                skippedSegments\n            );\n        } else {\n            logger.debug('Path generation completed - generated: %d points', totalGenerated);\n        }\n    }\n\n    /**\n     * Generate coordinates between two points at regular intervals\n     * @param coordinate1 - Start coordinate\n     * @param coordinate2 - End coordinate\n     * @param step - Distance between points in meters\n     */\n    private *generateCoordinatesBetween(\n        coordinate1: Coordinates,\n        coordinate2: Coordinates,\n        step: number\n    ): Generator<CoordinatesElevation, void, unknown> {\n        const distance = Distance.haversine(coordinate1, coordinate2);\n\n        // Always yield the start point\n        yield asCoordinatesElevation(coordinate1);\n\n        if (distance <= step) {\n            // If distance is less than step, just yield the end point\n            yield asCoordinatesElevation(coordinate2);\n            return;\n        }\n\n        // Calculate number of intermediate points\n        const numSteps = Math.floor(distance / step);\n\n        // Linear interpolation in lat/lng space (not great circle)\n        const latDiff = coordinate2.latitude - coordinate1.latitude;\n        const lonDiff = coordinate2.longitude - coordinate1.longitude;\n\n        for (let i = 1; i <= numSteps; i++) {\n            const fraction = (i * step) / distance;\n            yield {\n                latitude: coordinate1.latitude + latDiff * fraction,\n                longitude: coordinate1.longitude + lonDiff * fraction,\n                elevation: 0,\n            };\n        }\n\n        // Always yield the end point\n        yield asCoordinatesElevation(coordinate2);\n    }\n}\n","import { createLogger, Logger, LogLevel } from '../../utils';\n\nconst logger: Logger = createLogger('tile/cache/ReentrantLock');\n// ============================================================================\n// REENTRANT LOCK - Concurrency Control with Semaphore\n// ============================================================================\n\n/**\n * Reentrant lock with semaphore for limiting concurrent operations\n * Features:\n * - Request deduplication (multiple requests for same key)\n * - Concurrency limiting (max operations based on cache size)\n * - Race condition protection\n * - Proper cleanup and resource management\n */\nexport class ReentrantLock<T> {\n    private readonly locks = new Map<string, Promise<T>>();\n    private readonly maxConcurrent: number;\n    private loadingCount: number = 0;\n    private readonly waitQueue: Array<() => void> = [];\n\n    // ========================================================================\n    // CONSTRUCTOR\n    // ========================================================================\n\n    constructor(maxConcurrent: number) {\n        this.maxConcurrent = maxConcurrent;\n    }\n\n    // ========================================================================\n    // PUBLIC API\n    // ========================================================================\n\n    /**\n     * Acquire lock for key with deduplication and concurrency limiting\n     * @param key - Unique identifier for the operation\n     * @param fn - Function to execute if not already running\n     * @returns Promise resolving to the operation result\n     */\n    public async acquire(key: string, fn: () => Promise<T>): Promise<T> {\n        logger.debug(\n            '%s: Lock acquire requested (active: %d/%d, queued: %d)',\n            key,\n            this.loadingCount,\n            this.maxConcurrent,\n            this.waitQueue.length\n        );\n\n        // First check if we already have this key being loaded (deduplication)\n        if (this.locks.has(key)) {\n            logger.debug(\n                '%s: Lock deduplication - already loading, returning existing promise',\n                key\n            );\n            return this.locks.get(key)!;\n        }\n\n        // Wait for a loading slot for this new unique operation\n        await this.acquireLoadingSlot(key);\n\n        // Double-check after acquiring slot (race condition protection)\n        if (this.locks.has(key)) {\n            logger.debug(\n                '%s: Lock race condition - already loading after slot acquired, releasing slot',\n                key\n            );\n            this.releaseLoadingSlot(key);\n            return this.locks.get(key)!;\n        }\n\n        // Create the promise with proper cleanup\n        logger.debug('%s: Lock creating new promise', key);\n        const promise = (async () => {\n            try {\n                logger.debug('%s: Promise executing function', key);\n                const result = await fn();\n                logger.debug('%s: Promise resolved successfully', key);\n                return result;\n            } catch (error) {\n                logger.error('%s: Promise rejected - %o', key, error);\n                throw error;\n            } finally {\n                logger.debug('%s: Promise cleanup - removing lock and releasing slot', key);\n                this.locks.delete(key);\n                this.releaseLoadingSlot(key);\n            }\n        })();\n\n        this.locks.set(key, promise);\n        logger.debug('%s: Lock registered promise (total locks: %d)', key, this.locks.size);\n        return promise;\n    }\n\n    // ========================================================================\n    // PRIVATE - SEMAPHORE OPERATIONS\n    // ========================================================================\n\n    /**\n     * Acquire a loading slot (semaphore acquire)\n     */\n    private async acquireLoadingSlot(key: string): Promise<void> {\n        if (this.loadingCount < this.maxConcurrent) {\n            this.loadingCount++;\n            logger.debug(\n                '%s: Semaphore acquired slot immediately (%d/%d active, %d queued)',\n                key,\n                this.loadingCount,\n                this.maxConcurrent,\n                this.waitQueue.length\n            );\n            return;\n        }\n\n        logger.debug(\n            '%s: Semaphore waiting for slot (%d/%d active, %d queued)',\n            key,\n            this.loadingCount,\n            this.maxConcurrent,\n            this.waitQueue.length\n        );\n\n        logger.timeLevel(LogLevel.DEBUG, key);\n\n        // Wait until a slot becomes available\n        return new Promise<void>((resolve: () => void) => {\n            this.waitQueue.push(() => {\n                logger.timeEndLevel(LogLevel.DEBUG, key);\n                this.loadingCount++;\n                logger.debug(\n                    '%s: Semaphore acquired slot after waiting (%d/%d active, %d queued)',\n                    key,\n                    this.loadingCount,\n                    this.maxConcurrent,\n                    this.waitQueue.length\n                );\n                resolve();\n            });\n        });\n    }\n\n    /**\n     * Release a loading slot (semaphore release)\n     */\n    private releaseLoadingSlot(key: string): void {\n        if (this.waitQueue.length > 0) {\n            logger.debug(\n                '%s: Semaphore: releasing slot to waiting request (%d/%d active, %d queued)',\n                key,\n                this.loadingCount,\n                this.maxConcurrent,\n                this.waitQueue.length\n            );\n            const next = this.waitQueue.shift();\n            if (next) {\n                next(); // This will increment loadingCount in the queued resolver\n            }\n        } else {\n            this.loadingCount--;\n            logger.debug(\n                '%s: Semaphore: released slot (%d/%d active, %d queued)',\n                key,\n                this.loadingCount,\n                this.maxConcurrent,\n                this.waitQueue.length\n            );\n        }\n    }\n}\n","import { createLogger, Logger, LogLevel } from '../../utils';\nimport { ReentrantLock } from './ReentrantLock';\nconst logger: Logger = createLogger('tile/cache/Cache');\n\n// ============================================================================\n// LRU CACHE - Memory-Efficient Caching with Concurrency Control\n// ============================================================================\n\n/**\n * LRU (Least Recently Used) cache with performance optimizations and cleanup support\n * Features:\n * - O(1) LRU operations using doubly-linked list structure\n * - Automatic eviction when capacity exceeded\n * - Concurrent loading with deduplication via ReentrantLock\n * - Optional cleanup function for resource management\n * - Thread-safe operations with proper error handling\n */\nexport class Cache<K, T> {\n    private readonly maxSize: number;\n    private readonly cache: Map<string, T>;\n    private readonly keyMapper: (key: K) => string;\n    private readonly valueBuilder: (key: K) => Promise<T>;\n    private readonly cleanupFn?: (value: T) => void;\n\n    // Using LinkedList-like structure for true O(1) LRU operations\n    private readonly lruOrder: Map<string, { prev: string | null; next: string | null }>;\n    private head: string | null = null;\n    private tail: string | null = null;\n\n    // Concurrency control\n    private readonly lock: ReentrantLock<T>;\n\n    // ========================================================================\n    // CONSTRUCTOR & VALIDATION\n    // ========================================================================\n\n    constructor(\n        maxSize: number,\n        keyMapper: (key: K) => string,\n        valueBuilder: (key: K) => Promise<T>,\n        cleanupFn?: (value: T) => void\n    ) {\n        if (maxSize <= 0) {\n            throw new Error('Cache size must be greater than 0');\n        }\n\n        this.maxSize = maxSize;\n        this.keyMapper = keyMapper;\n        this.valueBuilder = valueBuilder;\n        this.cleanupFn = cleanupFn;\n        this.cache = new Map();\n        this.lruOrder = new Map();\n        this.lock = new ReentrantLock<T>(maxSize);\n    }\n\n    // ========================================================================\n    // PUBLIC API - CACHE OPERATIONS\n    // ========================================================================\n\n    public getDirect(k: K): T | undefined {\n        const key = this.keyMapper(k);\n        const cachedItem = this.cache.get(key);\n\n        if (cachedItem) {\n            this.moveToFront(key);\n            return cachedItem;\n        }\n        return undefined;\n    }\n\n    /**\n     * Get item from cache or build if not present\n     * @param k - Key to retrieve\n     * @returns Promise resolving to cached or newly built value\n     */\n    public async get(k: K): Promise<T> {\n        const key = this.keyMapper(k);\n        const cachedItem = this.cache.get(key);\n\n        if (cachedItem) {\n            this.moveToFront(key);\n            return cachedItem;\n        }\n        logger.debug('%s miss', key);\n\n        return this.lock.acquire(key, async () => {\n            const existing = this.cache.get(key);\n            if (existing) {\n                logger.debug('%s Missed at first but now OK', key);\n                this.moveToFront(key);\n                return existing;\n            }\n\n            logger.info('%s loading', key);\n            logger.timeLevel(LogLevel.INFO, key);\n            const newItem = await this.valueBuilder(k);\n            logger.info('%s loaded', key);\n            logger.timeEndLevel(LogLevel.INFO, key);\n            this.set(key, newItem);\n            return newItem;\n        });\n    }\n\n    /**\n     * Clear all cached items\n     */\n    public clear(): void {\n        logger.debug('clear');\n        // Call cleanup function for all cached items\n        if (this.cleanupFn) {\n            for (const value of this.cache.values()) {\n                this.cleanupFn(value);\n            }\n        }\n\n        this.cache.clear();\n        this.lruOrder.clear();\n        this.head = null;\n        this.tail = null;\n    }\n\n    // ========================================================================\n    // PROTECTED API - INSPECTION METHODS\n    // ========================================================================\n\n    /**\n     * Check if item exists in cache\n     * @param k - Key to check\n     * @returns True if key exists in cache\n     */\n    protected has(k: K): boolean {\n        const key = this.keyMapper(k);\n        return this.cache.has(key);\n    }\n\n    /**\n     * Get all cached keys\n     * @returns Array of all cached keys\n     */\n    protected getKeys(): string[] {\n        return Array.from(this.cache.keys());\n    }\n\n    /**\n     * Get the least recently used keys in order\n     * @param count - Maximum number of keys to return\n     * @returns Array of LRU keys from least to most recently used\n     */\n    protected getLRUKeys(count: number = 10): string[] {\n        const result: string[] = [];\n        let current = this.tail; // Start from tail (least recently used)\n\n        while (current && result.length < count) {\n            result.push(current);\n            const node = this.lruOrder.get(current);\n            current = node?.prev || null;\n        }\n\n        return result;\n    }\n\n    // ========================================================================\n    // PRIVATE - CACHE STORAGE OPERATIONS\n    // ========================================================================\n\n    /**\n     * Store item in cache with automatic eviction\n     */\n    private set(key: string, value: T): void {\n        // If cache is full, remove least recently used item\n        if (this.cache.size >= this.maxSize) {\n            this.evictLeastRecentlyUsed();\n        }\n\n        // Add new item\n        this.cache.set(key, value);\n        this.addToFront(key);\n    }\n\n    /**\n     * Remove item from cache with cleanup\n     */\n    private delete(key: string): boolean {\n        logger.debug('%s delete', key);\n        if (!this.cache.has(key)) {\n            return false;\n        }\n\n        const value = this.cache.get(key);\n        this.cache.delete(key);\n        this.removeFromLRU(key);\n\n        // Call cleanup function if provided\n        if (value && this.cleanupFn) {\n            this.cleanupFn(value);\n        }\n\n        return true;\n    }\n\n    // ========================================================================\n    // PRIVATE - LRU EVICTION OPERATIONS\n    // ========================================================================\n\n    /**\n     * Remove the least recently used item to make space\n     */\n    private evictLeastRecentlyUsed(): void {\n        if (!this.tail) {\n            return;\n        }\n\n        const lruKey = this.tail;\n        this.delete(lruKey);\n    }\n\n    // ========================================================================\n    // PRIVATE - LRU LINKED LIST OPERATIONS\n    // ========================================================================\n\n    /**\n     * Add a key to the front of the LRU list (most recently used)\n     */\n    private addToFront(key: string): void {\n        const node = { prev: null, next: this.head };\n        this.lruOrder.set(key, node);\n\n        if (this.head) {\n            const headNode = this.lruOrder.get(this.head)!;\n            headNode.prev = key;\n        } else {\n            // First node\n            this.tail = key;\n        }\n\n        this.head = key;\n    }\n\n    /**\n     * Move an existing key to the front of the LRU list\n     */\n    private moveToFront(key: string): void {\n        if (this.head === key) {\n            return; // Already at front\n        }\n\n        // Remove from current position\n        this.removeFromLRU(key);\n\n        // Add to front\n        this.addToFront(key);\n    }\n\n    /**\n     * Remove a key from the LRU doubly-linked list\n     */\n    private removeFromLRU(key: string): void {\n        const node = this.lruOrder.get(key);\n        if (!node) {\n            return;\n        }\n\n        // Update previous node's next pointer\n        if (node.prev) {\n            const prevNode = this.lruOrder.get(node.prev)!;\n            prevNode.next = node.next;\n        } else {\n            // This was the head\n            this.head = node.next;\n        }\n\n        // Update next node's prev pointer\n        if (node.next) {\n            const nextNode = this.lruOrder.get(node.next)!;\n            nextNode.prev = node.prev;\n        } else {\n            // This was the tail\n            this.tail = node.prev;\n        }\n\n        this.lruOrder.delete(key);\n    }\n}\n","export { Cache } from './Cache';\n","import { Tile } from '..';\nimport { TileCoordinates } from '../../types';\nimport { createLogger, Logger, LogLevel } from '../../utils';\nimport { TileFetcher } from './TileFetcher';\n\nconst logger: Logger = createLogger('tile/fetcher/TileLoader');\n\nexport class TileLoader {\n    constructor(\n        private tileUrlTemplate: string,\n        private tileFetcher: TileFetcher\n    ) {}\n\n    // ========================================================================\n    // PUBLIC API\n    // ========================================================================\n\n    public async loadTile(tileCoords: TileCoordinates): Promise<Tile> {\n        const tileKey = `${tileCoords.z}/${tileCoords.x}/${tileCoords.y}`;\n        const tileUrl = this.getTileUrl(tileCoords);\n\n        const fetchTimer = `fetch-${tileKey}`;\n        logger.timeLevel(LogLevel.DEBUG, fetchTimer);\n        try {\n            const tile = await this.tileFetcher.fetchTile(tileUrl);\n            logger.timeEndLevel(LogLevel.DEBUG, fetchTimer);\n            return tile;\n        } catch (error) {\n            logger.timeEndLevel(LogLevel.DEBUG, fetchTimer);\n            if (error instanceof Error) {\n                throw new Error(`Failed to fetch tile from ${tileUrl}: ${error.message}`, {\n                    cause: error,\n                });\n            }\n            throw new Error(`Failed to fetch tile from ${tileUrl}: Unknown error`, {\n                cause: error,\n            });\n        }\n    }\n\n    // ========================================================================\n    // PRIVATE\n    // ========================================================================\n\n    private getTileUrl(tileCoords: TileCoordinates): string {\n        const tileKey = `${tileCoords.z}/${tileCoords.x}/${tileCoords.y}`;\n        const fetchTimer = `fetch-${tileKey}`;\n        logger.timeLevel(LogLevel.DEBUG, fetchTimer);\n        return this.tileUrlTemplate\n            .replace('{z}', tileCoords.z.toString())\n            .replace('{x}', tileCoords.x.toString())\n            .replace('{y}', tileCoords.y.toString());\n    }\n}\n","import { createLogger, Logger } from '../../utils';\n\nconst logger: Logger = createLogger('tile/fetcher/CanvasPool');\n\n// ============================================================================\n// CANVAS POOL - Resource Management\n// ============================================================================\n\n/**\n * Canvas pool for efficient canvas reuse and memory management\n * Automatically trims excess canvases after idle period\n */\nexport class CanvasPool<T> {\n    private readonly builder: () => T;\n    private available: T[] = [];\n    private readonly idleSize: number = 5;\n    private readonly idleTimeout: number = 30000; // 30 seconds\n    private idleTimer: ReturnType<typeof setTimeout> | null = null;\n    private totalCreated: number = 0;\n    private totalAcquired: number = 0;\n    private totalReleased: number = 0;\n\n    constructor(builder: () => T) {\n        this.builder = builder;\n    }\n\n    /**\n     * Acquire a canvas from the pool (creates new if none available)\n     */\n    public acquire(): T {\n        this.totalAcquired++;\n        let canvas = this.available.pop();\n\n        if (!canvas) {\n            canvas = this.builder();\n            this.totalCreated++;\n            logger.debug(\n                'Canvas created - new canvas (total created: %d, pool size: %d)',\n                this.totalCreated,\n                this.available.length\n            );\n        } else {\n            logger.debug(\n                'Canvas acquired from pool (pool size: %d → %d, total acquired: %d)',\n                this.available.length + 1,\n                this.available.length,\n                this.totalAcquired\n            );\n        }\n\n        this._resetIdleTimer();\n        return canvas;\n    }\n\n    /**\n     * Return a canvas to the pool for reuse\n     */\n    public release(canvas: T): void {\n        if (canvas) {\n            this.totalReleased++;\n            this.available.push(canvas);\n            logger.debug(\n                'Canvas released to pool (pool size: %d → %d, total released: %d)',\n                this.available.length - 1,\n                this.available.length,\n                this.totalReleased\n            );\n            this._resetIdleTimer();\n        } else {\n            logger.warn('Canvas release attempted with null/undefined canvas');\n        }\n    }\n\n    /**\n     * Reset the idle timer for automatic cleanup\n     */\n    private _resetIdleTimer(): void {\n        if (this.idleTimer) {\n            clearTimeout(this.idleTimer);\n            logger.debug('Idle timer reset - previous timer cleared');\n        } else {\n            logger.debug('Idle timer started - %d ms until auto-trim', this.idleTimeout);\n        }\n        this.idleTimer = setTimeout(() => this._trim(), this.idleTimeout);\n    }\n\n    /**\n     * Trim excess canvases to prevent memory buildup\n     */\n    private _trim(): void {\n        const initialSize = this.available.length;\n        let trimmed = 0;\n\n        if (initialSize > this.idleSize) {\n            logger.debug(\n                'Auto-trim triggered - pool size %d exceeds idle limit %d',\n                initialSize,\n                this.idleSize\n            );\n\n            while (this.available.length > this.idleSize) {\n                this.available.pop();\n                trimmed++;\n            }\n\n            logger.info(\n                'Canvas pool trimmed - removed %d canvases (pool size: %d → %d)',\n                trimmed,\n                initialSize,\n                this.available.length\n            );\n        } else {\n            logger.debug(\n                'Auto-trim skipped - pool size %d within idle limit %d',\n                initialSize,\n                this.idleSize\n            );\n        }\n\n        // Clear the timer since this trim cycle is complete\n        this.idleTimer = null;\n    }\n}\n","export type { TileFetcher } from './TileFetcher';\nexport { CanvasPool } from './CanvasPool';\nexport { TileLoader } from './TileLoader';\n","import { ImageData } from 'canvas';\nimport { Tile } from '../..';\nimport { RGBColor } from '../../../types';\nexport class NodeTile extends Tile {\n    constructor(readonly data: ImageData) {\n        super(data.width, data.height);\n    }\n    public close() {}\n\n    /**\n     * Extract RGB values from ImageData at specific pixel position\n     * @param imageData - Image data from terrain tile\n     * @param position - Pixel coordinates within the tile\n     * @returns RGB color values for elevation decoding\n     */\n    public getRGBFromImageData(index: number): RGBColor {\n        return {\n            red: this.data.data[index],\n            green: this.data.data[index + 1],\n            blue: this.data.data[index + 2],\n            // Alpha channel (index + 3) is ignored for Terrarium encoding\n        };\n    }\n}\n","import { Canvas, createCanvas, loadImage } from 'canvas';\nimport { CanvasPool, TileFetcher } from '..';\nimport { Tile } from '../..';\nimport { NodeTile } from './NodeJsTile';\n\nexport class NodeJsTileFetcher implements TileFetcher {\n    private readonly canvasPool: CanvasPool<Canvas>;\n\n    constructor() {\n        this.canvasPool = new CanvasPool<Canvas>(() => createCanvas(256, 256));\n    }\n\n    /**\n     * Fetch a tile image and return both ImageData and ImageBitmap for memory management\n     * @param url - The URL of the tile to fetch\n     * @param tileKey - The tile identifier for logging\n     * @returns Promise<Tile> - Object containing ImageData and ImageBitmap\n     */\n    public async fetchTile(url: string): Promise<Tile> {\n        const image = await loadImage(url);\n        // Acquire canvas from pool\n        const canvas = this.canvasPool.acquire();\n\n        try {\n            // Resize canvas to match image dimensions\n            if (canvas.width !== image.width) {\n                canvas.width = image.width;\n            }\n            if (canvas.height !== image.height) {\n                canvas.height = image.height;\n            }\n\n            const ctx = canvas.getContext('2d');\n\n            ctx.drawImage(image, 0, 0);\n            const data = ctx.getImageData(0, 0, image.width, image.height);\n            return new NodeTile(data);\n        } finally {\n            // Always return canvas to pool for reuse\n            this.canvasPool.release(canvas);\n        }\n    }\n}\n","import { Cache } from './cache';\nimport { TileFetcher } from './fetcher/TileFetcher';\nimport { TileLoader } from './fetcher/TileLoader';\nimport { Tile } from './Tile';\nimport type { TileCoordinates } from '../types';\n\nexport class TileManager {\n    private cache: Cache<TileCoordinates, Tile> | undefined;\n\n    constructor(\n        private readonly tileUrlTemplate: string,\n        private readonly cacheSize: number\n    ) {}\n\n    public getTileDirect(tileCoords: TileCoordinates): Tile | undefined {\n        if (this.cache) {\n            return this.cache.getDirect(tileCoords);\n        }\n        return undefined;\n    }\n\n    public async getTile(tileCoords: TileCoordinates): Promise<Tile> {\n        if (this.cache) {\n            return await this.cache.get(tileCoords);\n        }\n        return Promise.reject(new Error('Cache not initialized'));\n    }\n\n    async initCache(): Promise<Cache<TileCoordinates, Tile>> {\n        if (!this.cache) {\n            let tileFetcher: TileFetcher;\n            if (__NODE__) {\n                const { NodeJsTileFetcher } = await import('./fetcher/nodejs/NodeJsTileFetcher');\n                tileFetcher = new NodeJsTileFetcher();\n            } else {\n                const { BrowserTileFetcher } = await import('./fetcher/browser/BrowserTileFetcher');\n                tileFetcher = new BrowserTileFetcher();\n            }\n            // Create cache with cleanup function to close ImageBitmaps\n            const cleanupFunction = (cachedTile: Tile) => cachedTile.close();\n\n            const tileLoader = new TileLoader(this.tileUrlTemplate, tileFetcher);\n\n            const createdCache = new Cache<TileCoordinates, Tile>(\n                this.cacheSize,\n                tileCoords => `${tileCoords.z}/${tileCoords.x}/${tileCoords.y}`,\n                tileCoords => tileLoader.loadTile(tileCoords),\n                cleanupFunction\n            );\n            this.cache = createdCache;\n            return createdCache;\n        } else {\n            return this.cache;\n        }\n    }\n}\n","import { Pixel, RGBColor } from '../types';\n\nexport abstract class Tile {\n    readonly cache: Float64Array;\n    constructor(\n        readonly width: number,\n        readonly height: number\n    ) {\n        this.cache = new Float64Array(width * height);\n        this.cache.fill(NaN);\n    }\n\n    abstract close(): void;\n    abstract getRGBFromImageData(index: number): RGBColor;\n\n    getElevation(position: Pixel): number {\n        // Input validation\n        if (position.x < 0 || position.x >= this.width) {\n            throw new Error(\n                `Invalid x position: ${position.x}. Must be between 0 and ${this.width - 1}`\n            );\n        }\n\n        if (position.y < 0 || position.y >= this.height) {\n            throw new Error(\n                `Invalid y position: ${position.y}. Must be between 0 and ${this.height - 1}`\n            );\n        }\n\n        // Calculate pixel index in RGBA array (4 bytes per pixel)\n        const index = (position.y * this.width + position.x) * 4;\n        if (isNaN(this.cache[index])) {\n            const rgb = this.getRGBFromImageData(index);\n            const elevation = this.decodeElevation(rgb);\n            this.cache[index] = elevation;\n            return elevation;\n        } else {\n            return this.cache[index];\n        }\n    }\n\n    /**\n     * Decode elevation from RGB values using Terrarium encoding\n     * Formula: elevation = (red * 256 + green + blue / 256) - 32768\n     * @param rgb - RGB color values from terrain tile pixel\n     * @returns Elevation in meters, rounded to 2 decimal places\n     */\n    decodeElevation(rgb: RGBColor): number {\n        const elevation = rgb.red * 256 + rgb.green + rgb.blue / 256 - 32768;\n        return Math.round(elevation * 100) / 100; // Round to 2 decimal places for precision\n    }\n}\n","export { TileManager } from './TileManager';\nexport { Tile } from './Tile';\n","import { ElevationCalculator, BatchCalculator } from './calculator';\nimport { TileManager } from './tile';\nimport { createLogger, Logger } from './utils';\nimport type {\n    Coordinates,\n    CoordinatesElevation,\n    ElevationProviderConfig,\n    Attribution,\n    SetElevationsOptions,\n    GetElevationsAlongOptions,\n    GetElevationOptions,\n} from './types';\n\nconst logger: Logger = createLogger('ElevationProvider');\n\n/**\n * Main API class for retrieving elevation data from geographic coordinates\n */\nexport class ElevationProvider {\n    private readonly config: Required<ElevationProviderConfig>;\n    private readonly tileManager: TileManager;\n    private readonly calculator: ElevationCalculator;\n    private readonly batchCalculator: BatchCalculator;\n\n    // ============================================================================\n    // CONSTRUCTOR & CONFIGURATION\n    // ============================================================================\n\n    constructor(config: ElevationProviderConfig = {}) {\n        this.config = {\n            zoomLevel: config.zoomLevel ?? 12,\n            cacheSize: config.cacheSize ?? 100,\n            tileUrlTemplate:\n                config.tileUrlTemplate ?? 'https://tiles.mapterhorn.com/{z}/{x}/{y}.webp',\n            tileSize: config.tileSize ?? 512,\n            attribution: config.attribution ?? {\n                text: 'Mapterhorn elevation data. See mapterhorn.com/attribution/ for details.',\n                url: 'https://mapterhorn.com/attribution/',\n            },\n        };\n        logger.dir('Config :', this.config);\n\n        this.validateConfig();\n        this.tileManager = new TileManager(this.config.tileUrlTemplate, this.config.cacheSize);\n        this.calculator = new ElevationCalculator(this.tileManager, this.config.tileSize);\n        this.batchCalculator = new BatchCalculator(this.calculator);\n    }\n\n    /**\n     * Get current configuration\n     */\n    public getConfig(): Readonly<Required<ElevationProviderConfig>> {\n        return { ...this.config };\n    }\n\n    /**\n     * Get attribution information for elevation data\n     */\n    public getAttribution(): Attribution {\n        return this.config.attribution;\n    }\n\n    // ============================================================================\n    // PUBLIC API - SINGLE COORDINATE METHODS\n    // ============================================================================\n\n    /**\n     * Get elevation at specific coordinates\n     * @param latitude - Latitude in decimal degrees\n     * @param longitude - Longitude in decimal degrees\n     * @param options - Optional parameters\n     */\n    public async getElevation(\n        latitude: number,\n        longitude: number,\n        options?: GetElevationOptions\n    ): Promise<number> {\n        await this.tileManager.initCache();\n        const interpolation = options?.interpolation ?? true;\n        const coords: Coordinates = { latitude, longitude };\n        return await this.calculator.getElevation(coords, this.config.zoomLevel, interpolation);\n    }\n\n    // ============================================================================\n    // PUBLIC API - BULK COORDINATE METHODS\n    // ============================================================================\n\n    /**\n     * Get elevations for multiple coordinates from an interable\n     * @param coordinates - Iteratable of coordinates\n     * @param options - Optional parameters\n     */\n    public async setElevations(\n        coordinates: Iterable<Coordinates>,\n        options?: SetElevationsOptions\n    ): Promise<void> {\n        await this.tileManager.initCache();\n        const interpolation = options?.interpolation ?? true;\n        await this.batchCalculator.setElevations(coordinates, this.config.zoomLevel, interpolation);\n    }\n\n    /**\n     * Get elevations along a path defined by multiple coordinates\n     * @param path - Array of coordinates defining the path\n     * @param options - Optional parameters\n     */\n    public async getElevationsAlong(\n        path: Coordinates[],\n        options?: GetElevationsAlongOptions\n    ): Promise<CoordinatesElevation[]> {\n        await this.tileManager.initCache();\n        const step = options?.step ?? 10;\n        const minDistance = options?.minDistance ?? 1;\n        const interpolation = options?.interpolation ?? true;\n        const smoothingOptions = options?.smoothingOptions;\n        const filterOptions = options?.filterOptions;\n        return this.batchCalculator.getElevationsAlong(\n            path,\n            this.config.zoomLevel,\n            step,\n            minDistance,\n            interpolation,\n            smoothingOptions,\n            filterOptions\n        );\n    }\n\n    // ============================================================================\n    // PRIVATE - VALIDATION\n    // ============================================================================\n\n    private validateConfig(): void {\n        const { zoomLevel, cacheSize } = this.config;\n\n        if (!Number.isInteger(zoomLevel) || zoomLevel < 0 || zoomLevel > 15) {\n            throw new Error(\n                `Invalid zoom level: ${zoomLevel}. Must be an integer between 0 and 15`\n            );\n        }\n\n        if (!Number.isInteger(cacheSize) || cacheSize <= 0) {\n            throw new Error(`Invalid cache size: ${cacheSize}. Must be a positive integer`);\n        }\n\n        const { tileSize } = this.config;\n        if (!Number.isInteger(tileSize) || tileSize <= 0 || (tileSize & (tileSize - 1)) !== 0) {\n            throw new Error(`Invalid tile size: ${tileSize}. Must be a positive power of 2`);\n        }\n    }\n}\n"],"mappings":";;;;;;;;;;;;AASA,SAAgB,EAAS,GAAyB;AAC9C,QAAQ,IAAU,KAAK,KAAM;;AAMjC,SAAgB,EAAgB,GAAsB;AAClD,QAAO,KAAO,YAAY,KAAO;;AAMrC,SAAgB,EAAiB,GAAsB;AACnD,QAAO,KAAO,QAAQ,KAAO;;AAMjC,SAAgB,EAAiB,GAAuB;AACpD,QAAO,OAAO,UAAU,EAAK,IAAI,KAAQ,KAAK,KAAQ;;AAG1D,SAAgB,EAAe,GAAc,GAAyB;CAClE,IAAI,EAAE,MAAG,SAAM,GACT,IAAO,EAAM,MACf,IAAQ,EAAK,GACb,IAAQ,EAAK,GACX,IAAI,EAAK;AAcf,CAZI,IAAI,MACJ,KAAK,GACL,MAEA,KAAK,MACL,KAAK,GACL,KAAS,IAET,IAAI,MACJ,KAAK,GACL,MAEA,KAAK,MACL,KAAK,GACL,KAAS;CAGb,IAAM,IAAmB,KAAG,IAAK;AAIjC,QAHA,IAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAS,EAAM,CAAC,EAC7C,IAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAS,EAAM,CAAC,EAEtC;EAAE,MAAM;GAAE;GAAG,GAAG;GAAO,GAAG;GAAO;EAAE;EAAG;EAAG;;AAGpD,SAAgB,EAAuB,GAAqB,GAAiC;AAEzF,KAAI,CAAC,EAAgB,EAAO,SAAS,CACjC,OAAU,MACN,qBAAqB,EAAO,SAAS,wCACxC;AAGL,KAAI,CAAC,EAAiB,EAAO,UAAU,CACnC,OAAU,MAAM,sBAAsB,EAAO,UAAU,gCAAgC;AAG3F,KAAI,CAAC,EAAiB,EAAE,CACpB,OAAU,MAAM,uBAAuB,EAAE,4BAA4B;CAIzE,IAAM,IAAM,EAAS,EAAO,SAAS,EAC/B,IAAa,KAAG,GAChB,KAAW,EAAO,YAAY,OAAO,MAAO,GAC5C,KAAW,IAAI,KAAK,IAAI,KAAK,IAAI,EAAI,GAAG,IAAI,KAAK,IAAI,EAAI,CAAC,GAAG,KAAK,MAAM,IAAK,GAE/E,IAAI,KAAK,MAAM,EAAO,EACtB,IAAI,KAAK,MAAM,EAAO,EAGpB,IAAU,IAAI;AAIpB,QAHA,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAS,EAAE,CAAC,EACrC,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAS,EAAE,CAAC,EAE9B;EACH;EACA;EACA;EACA;EACA;EACH;;AAGL,SAAgB,EAAkB,GAAqB,GAA4B;CAC/E,IAAM,IAAO,EAAuB,GAAQ,EAAE;AAC9C,QAAO;EACH,GAAG,EAAK;EACR,GAAG,EAAK;EACR,GAAG,EAAK;EACX;;AASL,SAAgB,EAAQ,GAAqB,GAAW,GAAyB;CAC7E,IAAM,IAAO,EAAuB,GAAQ,EAAE,EAGxC,IAAI,KAAK,OAAO,EAAK,SAAS,EAAK,KAAK,EAAS,EACjD,IAAI,KAAK,OAAO,EAAK,SAAS,EAAK,KAAK,EAAS;AAEvD,QAAO;EACH,MAAM;GACF;GACA,GAAG,EAAK;GACR,GAAG,EAAK;GACX;EACD,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,IAAW,GAAG,EAAE,CAAC;EACzC,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,IAAW,GAAG,EAAE,CAAC;EAC5C;;;;AClIL,IAAa,IAAb,MAAiC;CAI7B,YAAY,GAA0B,IAAmB,KAAK;AAE1D,EADA,KAAK,cAAc,GACnB,KAAK,WAAW;;CAOpB,MAAa,aACT,GACA,GACA,IAAyB,IACV;AACf,MAAI;AACA,OAAI,EACA,QAAO,MAAM,KAAK,iCAAiC,GAAQ,EAAU;GAClE;IACH,IAAM,IAAQ,EAAQ,GAAQ,GAAW,KAAK,SAAS;AACvD,WAAO,MAAM,KAAK,sBAAsB,EAAM;;WAE7C,GAAO;AAIZ,SAHI,aAAiB,QACP,MAAM,4BAA4B,EAAM,WAAW,EAAE,OAAO,GAAO,CAAC,GAExE,MAAM,0CAA0C,EAAE,OAAO,GAAO,CAAC;;;CAQnF,MAAc,iCACV,GACA,GACe;EACf,IAAM,IAAQ,EAAQ,GAAQ,GAAW,KAAK,SAAS,EACjD,IAAa;GACf,MAAM,EAAM;GACZ,GAAG,EAAM;GACT,GAAG,EAAM;GACZ,EACK,IAAK,KAAK,MAAM,EAAW,EAAE,EAC7B,IAAK,KAAK,MAAM,EAAW,EAAE,EAC7B,IAAK,IAAK,GACV,IAAK,IAAK,GAEV,IAAK,EAAW,IAAI,GACpB,IAAK,EAAW,IAAI,GAEpB,IAAM,MAAM,KAAK,sBACnB,EAAe;GAAE,MAAM,EAAW;GAAM,GAAG;GAAI,GAAG;GAAI,EAAE,KAAK,SAAS,CACzE,EACK,IAAM,MAAM,KAAK,sBACnB,EAAe;GAAE,MAAM,EAAW;GAAM,GAAG;GAAI,GAAG;GAAI,EAAE,KAAK,SAAS,CACzE,EACK,IAAM,MAAM,KAAK,sBACnB,EAAe;GAAE,MAAM,EAAW;GAAM,GAAG;GAAI,GAAG;GAAI,EAAE,KAAK,SAAS,CACzE,EACK,IAAM,MAAM,KAAK,sBACnB,EAAe;GAAE,MAAM,EAAW;GAAM,GAAG;GAAI,GAAG;GAAI,EAAE,KAAK,SAAS,CACzE,EAEK,IAAM,KAAO,IAAI,KAAM,IAAM,GAC7B,IAAS,KAAO,IAAI,KAAM,IAAM;AACtC,SAAO,KAAO,IAAI,KAAM,IAAS;;CAMrC,MAAc,sBAAsB,GAA+B;EAC/D,IAAI,IAAa,KAAK,YAAY,cAAc,EAAM,KAAK;AAI3D,SAHA,AACI,MAAa,MAAM,KAAK,YAAY,QAAQ,EAAM,KAAK,EAEpD,EAAW,aAAa,EAAM;;;;;ACxE7C,SAAgB,EAAuB,GAAgD;AACnF,QAAO;EAAE,GAAG;EAAa,WAAW,EAAY,aAAa;EAAG;;;;;ACuOvD,CApOA,IAAb,MAAsB;;gBACqB;;;eACD;;;eACA;;;gBACC;;;gBACA;;IAGrC,IAAsD;EACxD,OAAO,EAAS;EAChB,MAAM,EAAS;EACf,MAAM,EAAS;EACf,OAAO,EAAS;EAChB,OAAO,EAAS;EACnB,EAEK,IAA4C;EAC9C,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACN,EAEK,IAA+C;EACjD,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACd,EASK,IAAN,MAAa;EAIT,YAAY,GAAmB;AAmBvB,GAlBJ,KAAK,YAAY,GAkBb,KAAK,QAAQ,EAAa;;EAIlC,UAAkB,GAA+B;AAC7C,UAAO,KAAS,KAAK;;EAGzB,MAAc,GAAsB,GAAe,GAAG,GAAuB;GACzE,IAAM,IAAS,IAAI,KAAK,UAAU,GAAG,EAAW,GAAO;AACvD,GAAI,OAAO,KAAY,WAEnB,EAAS,GAAO,GAAG,EAAO,GAAG,KAAW,GAAG,EAAe,GAG1D,EAAS,GAAO,GAAQ,GAAS,GAAG,EAAe;;EAI3D,IAAY,GAAsB,GAAe,GAAG,GAAuB;AACvE,GAAI,KAAK,UAAU,EAAM,IACrB,KAAK,MAAM,GAAO,GAAS,GAAG,EAAe;;EAQrD,MAAM,GAAe,GAAG,GAA6B;EAUrD,MAAM,GAAe,GAAG,GAA6B;EAUrD,KAAK,GAAe,GAAG,GAA6B;EAUpD,KAAK,GAAe,GAAG,GAA6B;AAChD,QAAK,IAAI,EAAS,MAAM,GAAS,GAAG,EAAe;;EAOvD,MAAM,GAAe,GAAG,GAA6B;AACjD,QAAK,IAAI,EAAS,OAAO,GAAS,GAAG,EAAe;;EAGxD,aAAqB,GAAsB,GAAuB;AAC9D,UAAO,IAAI,KAAK,UAAU,GAAG,EAAW,GAAO,IAAI;;EAGvD,OAAe,GAAsB,GAAqB;AACtD,WAAQ,KAAK,KAAK,aAAa,GAAO,EAAM,CAAC;;EAGjD,UAAkB,GAAsB,GAAqB;AACzD,WAAQ,QAAQ,KAAK,aAAa,GAAO,EAAM,CAAC;;EAOpD,UAAU,GAAsB,GAAqB;EAWrD,aAAa,GAAsB,GAAqB;EAYxD,KAAK,GAAqB;AACtB,QAAK,OAAO,EAAS,MAAM,EAAM;;EAMrC,QAAQ,GAAqB;AACzB,QAAK,UAAU,EAAS,MAAM,EAAM;;EAGxC,OAAe,GAAsB,GAAe,GAAW,GAAe;AAE1E,GADA,KAAK,MAAM,GAAO,UAAU,EAAQ,EACpC,QAAQ,IAAI,GAAK,EAAQ;;EAS7B,SAAS,GAAsB,GAAe,GAAW,GAAqB;EAc9E,IAAI,GAAe,GAAW,GAAqB;AAC/C,QAAK,OAAO,EAAS,MAAM,GAAS,GAAK,EAAQ;;EAMrD,QAAc;AACV,WAAQ,OAAO;;IAYV,KAAgB,MAA8B,IAAI,EAAO,EAAU;;ICrPjC;;;;;ACG/C,IAAA,IAAA;;;;GAYA,IAAA;;;GAUA,IAAA,EAAA,sBAAA,GAAA,EClBa,IAAb,MAAa,EAAS;CAOlB,OAAc,UAAU,GAAqB,GAA6B;EACtE,IAAM,IAAU,EAAO,WAAW,EAAe,YAC3C,IAAU,EAAO,WAAW,EAAe,YAC3C,KAAY,EAAO,WAAW,EAAO,YAAY,EAAe,YAChE,KAAY,EAAO,YAAY,EAAO,aAAa,EAAe,YAElE,IACF,KAAK,IAAI,IAAW,EAAE,GAAG,KAAK,IAAI,IAAW,EAAE,GAC/C,KAAK,IAAI,EAAQ,GAAG,KAAK,IAAI,EAAQ,GAAG,KAAK,IAAI,IAAW,EAAE,GAAG,KAAK,IAAI,IAAW,EAAE,EAErF,IAAI,IAAI,KAAK,MAAM,KAAK,KAAK,EAAE,EAAE,KAAK,KAAK,IAAI,EAAE,CAAC;AACxD,SAAO,EAAgB,cAAc;;CASzC,OAAc,YAAY,GAAkB,GAA0B;EAClE,IAAM,IAAK,EAAO,IAAI,EAAO,GACvB,IAAK,EAAO,IAAI,EAAO,GACvB,IAAK,EAAO,IAAI,EAAO;AAC7B,SAAO,KAAK,KAAK,IAAK,IAAK,IAAK,IAAK,IAAK,EAAG;;CAUjD,OAAc,iBACV,GACA,GACA,GACM;EACN,IAAM,IAAgB,EAAW,SAAS,EAAa,EACjD,IAAc,EAAM,SAAS,EAAa,EAG1C,IAAuB,EAAc,IAAI,EAAc;AAC7D,MAAI,MAAyB,EACzB,QAAO,EAAS,YAAY,GAAO,EAAa;EAIpD,IAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAY,IAAI,EAAc,GAAG,EAAqB,CAAC,EAGnF,IAAe,EAAa,IAAI,EAAc,SAAS,EAAE,CAAC;AAGhE,SAAO,EAAS,YAAY,GAAO,EAAa;;CAQpD,OAAc,oBAAoB,GAAiC;EAC/D,IAAM,IAAsB,CAAC,EAAE;AAE/B,OAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK;GACpC,IAAM,IAAkB,EAAS,UAAU,EAAO,IAAI,IAAI,EAAO,GAAG;AACpE,KAAU,KAAK,EAAU,IAAI,KAAK,EAAgB;;AAGtD,SAAO;;CAQX,OAAc,kBAAkB,GAA+B;AAC3D,MAAI,EAAO,SAAS,EAChB,QAAO;EAGX,IAAI,IAAgB;AACpB,OAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,IAC/B,MAAiB,EAAS,UAAU,EAAO,IAAI,IAAI,EAAO,GAAG;AAGjE,SAAO;;GCpGF,IAAb,MAAa,EAAS;CAClB,YACI,GACA,GACA,GACF;AADkB,EAFA,KAAA,IAAA,GACA,KAAA,IAAA,GACA,KAAA,IAAA;;CAMpB,WAAkB,GAAyB;EACvC,IAAM,IAAK,KAAK,IAAI,EAAM,GACpB,IAAK,KAAK,IAAI,EAAM,GACpB,IAAK,KAAK,IAAI,EAAM;AAC1B,SAAO,KAAK,MAAM,GAAI,GAAI,EAAG;;CAMjC,SAAgB,GAA2B;AACvC,SAAO,IAAI,EAAS,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAM,EAAE;;CAM7E,IAAW,GAA2B;AAClC,SAAO,IAAI,EAAS,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAM,EAAE;;CAM7E,SAAgB,GAA0B;AACtC,SAAO,IAAI,EAAS,KAAK,IAAI,GAAQ,KAAK,IAAI,GAAQ,KAAK,IAAI,EAAO;;CAM1E,IAAW,GAAyB;AAChC,SAAO,KAAK,IAAI,EAAM,IAAI,KAAK,IAAI,EAAM,IAAI,KAAK,IAAI,EAAM;;CAMhE,MAAa,GAA2B;AACpC,SAAO,IAAI,EACP,KAAK,IAAI,EAAM,IAAI,KAAK,IAAI,EAAM,GAClC,KAAK,IAAI,EAAM,IAAI,KAAK,IAAI,EAAM,GAClC,KAAK,IAAI,EAAM,IAAI,KAAK,IAAI,EAAM,EACrC;;CAML,YAA2B;AACvB,SAAO,KAAK,MAAM,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE;;CAM7C,YAA6B;EACzB,IAAM,IAAM,KAAK,WAAW;AAI5B,SAHI,MAAQ,IACD,IAAI,EAAS,GAAG,GAAG,EAAE,GAEzB,KAAK,SAAS,IAAI,EAAI;;CAMjC,kBAAyB,GAAwB,GAA8B;EAC3E,IAAM,IAAgB,EAAW,SAAS,EAAa,EACjD,IAAgB,EAAc,WAAW;AAG/C,MAAI,MAAkB,EAClB,QAAO,KAAK,WAAW,EAAa;EAOxC,IAAM,IAHc,KAAK,SAAS,EAGf,CAAY,IAAI,EAAc,IAAI,IAAgB,IAG/D,IAAoB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,EAAW,CAAC,EAGxD,IAAe,EAAa,IAAI,EAAc,SAAS,EAAkB,CAAC;AAGhF,SAAO,KAAK,WAAW,EAAa;;GC9F/B,IAAb,MAA2B;CAOvB,OAAc,OAAO,GAA0B,IAAwB,GAAa;EAEhF,IAAM,IAAU,EAAY,WAAW,KAAK,KAAM,KAC5C,IAAU,EAAY,YAAY,KAAK,KAAM,KAG7C,IAAuB,KAAiB,EAAY,aAAa,IAGjE,IAAS,KAAK,IAAI,EAAO,EACzB,IACF,EAAgB,kBAChB,KAAK,KAAK,IAAI,EAAgB,6BAA6B,IAAS,EAAO,EAGzE,IAAS,KAAK,IAAI,EAAO,EACzB,IAAS,KAAK,IAAI,EAAO,EACzB,IAAS,KAAK,IAAI,EAAO;AAO/B,SAAO,IAAI,GALA,IAAI,KAAwB,IAAS,IACrC,IAAI,KAAwB,IAAS,IAE3C,KAAK,IAAI,EAAgB,8BAA8B,KAAwB,EAExD;;CAShC,OAAc,aAAa,GAA4B,IAAwB,GAAe;AAC1F,SAAO,EAAY,KAAI,MAAS,KAAK,OAAO,GAAO,EAAc,CAAC;;;;;GC/C3B;AAE/C,IAAM,IAAiB,EAAa,uBAAuB,EAM9C,IAAb,MAA4B;CAQxB,OAAc,SACV,GACA,GACA,IAAwB,GACF;AAEtB,MADA,EAAO,KAAK,eAAe,EAAO,OAAO,EACrC,EAAO,UAAU,EAEjB,QADA,EAAO,KAAK,YAAY,EACjB,CAAC,GAAG,EAAO;AAEtB,IAAO,UAAU,EAAS,MAAM,WAAW;EAE3C,IAAM,IAAY,EAAO,SAAS,GAC5B,IAAqC,EAAE;AAG7C,IAAW,KAAK,EAAO,GAAG;EAG1B,IAAM,IAAqB,KAAK,kBAC5B,GACA,GACA,GACA,GACA,EACH;AAQD,SAPA,EAAW,KAAK,GAAG,EAAmB,EAGtC,EAAW,KAAK,EAAO,GAAW,EAElC,EAAO,aAAa,EAAS,MAAM,WAAW,EAC9C,EAAO,MAAM,oBAAoB,EAAW,OAAO,EAC5C;;CAYX,OAAe,kBACX,GACA,GACA,GACA,GACA,GACsB;EACtB,IAAI,IAAc,GACd,IAAW,IACT,IAAiC,EAAE,EAGnC,IAAY,EAAc,OAAO,EAAO,IAAa,EAAc,EACnE,IAAW,EAAc,OAAO,EAAO,IAAY,EAAc;AAGvE,OAAK,IAAI,IAAI,IAAa,GAAG,IAAI,GAAW,KAAK;GAE7C,IAAM,IADY,EAAc,OAAO,EAAO,IAAI,EACjC,CAAU,kBAAkB,GAAW,EAAS;AAEjE,GAAI,IAAW,MACX,IAAc,GACd,IAAW;;AAKnB,MAAI,IAAc,KAAa,MAAa,IAAI;AAE5C,OAAI,IAAW,IAAa,GAAG;IAC3B,IAAM,IAAc,KAAK,kBACrB,GACA,GACA,GACA,GACA,EACH;AACD,MAAO,KAAK,GAAG,EAAY;;AAO/B,OAHA,EAAO,KAAK,EAAO,GAAU,EAGzB,IAAY,IAAW,GAAG;IAC1B,IAAM,IAAe,KAAK,kBACtB,GACA,GACA,GACA,GACA,EACH;AACD,MAAO,KAAK,GAAG,EAAa;;;AAIpC,SAAO;;;;;GCpHgC;AAE/C,IAAM,IAAiB,EAAa,0BAA0B,EAMjD,IAAb,MAA+B;CAO3B,OAAc,OACV,GACA,IAAqB,IACC;AAGtB,MAFA,EAAO,MAAM,aAAa,EAAO,OAAO,EAEpC,EAAO,SAAS,EAAoB,qBAEpC,QADA,EAAO,MAAM,YAAY,EAClB;AAGX,MAAI,KAAc,EACd,OAAU,MAAM,wBAAwB,EAAW,oBAAoB;AAE3E,IAAO,UAAU,EAAS,MAAM,SAAS;EAGzC,IAAM,IAAY,EAAS,oBAAoB,EAAO,EAGhD,IAAyC,EAAE;AAEjD,OAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK;GACpC,IAAM,IAAoB,KAAK,qBAAqB,GAAG,GAAQ,GAAW,EAAW;AAErF,KAAe,KAAK;IAChB,GAAG,EAAO;IACV,WAAW;IACd,CAAC;;AAIN,SAFA,EAAO,aAAa,EAAS,MAAM,SAAS,EAErC;;CAWX,OAAe,qBACX,GACA,GACA,GACA,GACM;EACN,IAAM,IAAkB,EAAU,IAG9B,IAAa;AACjB,SAAO,IAAa,KAAK,IAAkB,EAAU,IAAa,MAAM,GACpE;EAGJ,IAAI,IAAW;AACf,SACI,IAAW,EAAO,SAAS,KAC3B,EAAU,IAAW,KAAK,KAAmB,GAE7C;EAIJ,IAAI,IAAc,GACd,IAAc;AAElB,OAAK,IAAI,IAAI,GAAY,KAAK,GAAU,KAAK;GAIzC,IAAM,IAAS,IAHa,KAAK,IAAI,EAAU,KAAK,EAGjC,GAAsB;AAGzC,GADA,KAAe,GACf,KAAe,EAAO,GAAG,YAAY;;AAMzC,SAAO,IAAc,IAAI,IAAc,IAAc,EAAO,GAAO;;GC/F9D,IAAb,MAAa,EAAK;CACd,aAAqB,KAAK,GAA4B;AAClD,MAAI;AAEA,UADA,MAAM,GACC,EAAE,IAAI,IAAe;WACvB,GAAG;AACR,UAAO;IAAE,IAAI;IAAgB,OAAO;IAAG;;;CAI/C,aAAqB,SAAS,GAAqC;EAC/D,IAAM,EAAE,QAAK,WAAQ,MAAM,QAAQ,KAC/B,EAAS,KAAK,GAAI,MAAQ,EAAG,MAAK,OAAQ;GAAE;GAAK;GAAK,EAAE,CAAC,CAC5D;AACD,QAAS,OAAO,GAAK,EAAE,EACnB,GAAI,GAGJ,OAAM,EAAI;;CAIlB,aAAa,QACT,GACA,GACA,IAAc,GACD;EACb,IAAM,IAAuB,EAAE;AAE/B,OAAK,IAAM,KAAQ,EAGf,CAFA,EAAS,KAAK,EAAK,KAAK,EAAG,EAAK,CAAC,CAAC,EAE9B,EAAS,UAAU,KACnB,MAAM,EAAK,SAAS,EAAS;AAIrC,SAAO,EAAS,SAAS,GACrB,OAAM,EAAK,SAAS,EAAS;;GC3BnC,IAAiB,EAAa,6BAA6B,EAEpD,IAAb,MAA6B;CAGzB,YAAY,GAA0C;AAClD,OAAK,sBAAsB;;CAG/B,MAAa,cACT,GACA,GACA,GACa;AACb,IAAO,MAAM,gBAAgB;EAC7B,IAAM,IAA+C,EAAE,EACjD,oBAAmD,IAAI,KAAK;AAElE,IAAO,UAAU,EAAS,OAAO,kBAAkB;EAEnD,IAAM,KAAW,MAAkC,GAAG,EAAK,EAAE,GAAG,EAAK,EAAE,GAAG,EAAK;AAG/E,OAAK,IAAM,KAAS,GAAa;GAC7B,IAAM,IAAO,EAAkB,GAAO,EAAU,EAC1C,IAAM,EAAQ,EAAK,EAErB,IAAQ,EAAc;AAM1B,GALK,MACD,IAAQ,EAAE,EACV,EAAc,KAAO,GACrB,EAAmB,IAAI,GAAK,EAAK,GAErC,EAAM,KAAK,EAAM;;EAErB,IAAM,IAAQ,MAAM,KAAK,EAAmB,QAAQ,CAAC;AAqBrD,EApBA,EAAO,aAAa,EAAS,OAAO,kBAAkB,EAEtD,EAAO,UAAU,EAAS,OAAO,iBAAiB,EAClD,MAAM,EAAK,QACP,GACA,OAAM,MAAQ;GACV,IAAM,IAAM,EAAQ,EAAK;AACzB,KAAO,UAAU,EAAS,OAAO,oBAAoB,EAAI;GACzD,IAAM,IAAwB,EAAc;AAC5C,QAAK,IAAM,KAAS,EAChB,GAAM,YAAY,MAAM,KAAK,oBAAoB,aAC7C,GACA,GACA,EACH;AAEL,KAAO,aAAa,EAAS,OAAO,oBAAoB,EAAI;KAEhE,GACH,EACD,EAAO,aAAa,EAAS,OAAO,iBAAiB;;CAYzD,MAAa,mBACT,GACA,GACA,GACA,GACA,GACA,GACA,GAC+B;EAC/B,IAAM,IAAY;AAYlB,MAXA,EAAO,UAAU,EAAS,MAAM,EAAU,EAE1C,EAAO,KACH,mFACA,EAAK,QACL,GACA,GACA,EACH,EAGG,EAAK,SAAS,EAEd,OADA,EAAO,MAAM,uDAAuD,EAAK,OAAO,EACtE,MAAM,2CAA2C;AAE/D,MAAI,KAAQ,EAER,OADA,EAAO,MAAM,gDAAgD,EAAK,EACxD,MAAM,sBAAsB,EAAK,SAAS;EAIxD,IAAI,IAAc,MAAM,KAAK,KAAK,yBAAyB,GAAM,GAAM,EAAY,CAAC;AAUpF,MATA,EAAO,MAAM,uCAAuC,EAAY,OAAO,EAGvE,EAAO,MAAM,gDAAgD,EAC7D,MAAM,KAAK,cAAc,GAAa,GAAW,EAAc,EAE/D,EAAO,MAAM,qDAAqD,EAAY,OAAO,EAGjF,GAAkB,YAAY,MAAQ,EAAY,UAAU,GAAG;GAC/D,IAAM,IAAa,EAAiB,cAAc,IAC5C,IAAgB,EAAY;AAElC,KAAO,MAAM,kDAAkD,EAAW;GAC1E,IAAM,IAAc;AAMpB,GALA,EAAO,UAAU,EAAS,OAAO,EAAY,EAE7C,IAAc,EAAkB,OAAO,GAAa,EAAW,EAE/D,EAAO,aAAa,EAAS,OAAO,EAAY,EAChD,EAAO,MACH,yCACA,GACA,EAAY,OACf;SACM,GAAkB,YAAY,MACrC,EAAO,MACH,4DACA,EAAY,OACf;AAIL,MAAI,GAAe,YAAY,MAAQ,EAAY,SAAS,GAAG;GAC3D,IAAM,IAAY,GAAe,aAAa,IACxC,IAAgB,GAAe,iBAAiB,GAChD,IAAgB,EAAY;AAElC,KAAO,MACH,yEACA,GACA,EACH;GACD,IAAM,IAAc;AACpB,KAAO,UAAU,EAAS,OAAO,EAAY;GAE7C,IAAM,IAAW,EAAe,SAAS,GAAa,GAAW,EAAc;AAmB/E,UAjBA,EAAO,aAAa,EAAS,OAAO,EAAY,EAChD,EAAO,MACH,0DACA,GACA,EAAS,UACN,IAAgB,EAAS,UAAU,IAAiB,KAAK,QAAQ,EAAE,CACzE,EAED,EAAO,aAAa,EAAS,MAAM,EAAU,EAC7C,EAAO,KACH,2FACA,EAAK,QACL,EAAS,QACT,GAAkB,SAClB,GAAe,QAClB,EAEM;SACA,GAAe,YAAY,MAClC,EAAO,MACH,4DACA,EAAY,OACf;AAYL,SATA,EAAO,aAAa,EAAS,MAAM,EAAU,EAC7C,EAAO,KACH,2FACA,EAAK,QACL,EAAY,QACZ,GAAkB,SAClB,GAAe,QAClB,EAEM;;CAQX,CAAS,yBACL,GACA,GACA,GAC8C;AAC9C,MAAI,EAAK,WAAW,GAAG;AACnB,KAAO,MAAM,wDAAwD,EAAK,OAAO;AACjF;;AAEJ,IAAO,MAAM,qDAAqD,EAAK,QAAQ,EAAK;EAEpF,IAAM,IAAgB;AAItB,EAHA,EAAO,UAAU,EAAS,OAAO,EAAc,EAG/C,MAAM,EAAuB,EAAK,GAAG;EACrC,IAAI,IAAiB,GACjB,IAAkB;AAEtB,OAAK,IAAI,IAAI,GAAG,IAAI,EAAK,SAAS,GAAG,KAAK;GACtC,IAAM,IAAkB,EAAS,UAAU,EAAK,IAAI,EAAK,IAAI,GAAG;AAGhE,OAAI,IAAkB,GAAa;AAE/B,IADA,KACA,EAAO,MACH,mEACA,IAAI,GACJ,GACA,EACH;AACD;;GAKJ,IAAI,IAAU;AACd,QAAK,IAAM,KAAS,KAAK,2BAA2B,EAAK,IAAI,EAAK,IAAI,IAAI,EAAK,EAAE;AAC7E,QAAI,GAAS;AACT,SAAU;AACV;;AAGJ,IADA,MAAM,GACN;;;AAKR,EAFA,EAAO,aAAa,EAAS,OAAO,EAAc,EAE9C,IAAkB,IAClB,EAAO,MACH,0EACA,GACA,EACH,GAED,EAAO,MAAM,oDAAoD,EAAe;;CAUxF,CAAS,2BACL,GACA,GACA,GAC8C;EAC9C,IAAM,IAAW,EAAS,UAAU,GAAa,EAAY;AAK7D,MAFA,MAAM,EAAuB,EAAY,EAErC,KAAY,GAAM;AAElB,SAAM,EAAuB,EAAY;AACzC;;EAIJ,IAAM,IAAW,KAAK,MAAM,IAAW,EAAK,EAGtC,IAAU,EAAY,WAAW,EAAY,UAC7C,IAAU,EAAY,YAAY,EAAY;AAEpD,OAAK,IAAI,IAAI,GAAG,KAAK,GAAU,KAAK;GAChC,IAAM,IAAY,IAAI,IAAQ;AAC9B,SAAM;IACF,UAAU,EAAY,WAAW,IAAU;IAC3C,WAAW,EAAY,YAAY,IAAU;IAC7C,WAAW;IACd;;AAIL,QAAM,EAAuB,EAAY;;;ACjSpC,IAfkC,EAEzC,IAAiB,EAAa,2BAA2B,EAalD,IAAb,MAA8B;EAU1B,YAAY,GAAuB;AAC/B,gCAVqB,IAAI,KAAyB,sBAEvB,oBACiB,EAAE,EAO9C,KAAK,gBAAgB;;EAazB,MAAa,QAAQ,GAAa,GAAkC;AAUhE,OATA,EAAO,MACH,0DACA,GACA,KAAK,cACL,KAAK,eACL,KAAK,UAAU,OAClB,EAGG,KAAK,MAAM,IAAI,EAAI,CAKnB,QAJA,EAAO,MACH,wEACA,EACH,EACM,KAAK,MAAM,IAAI,EAAI;AAO9B,OAHA,MAAM,KAAK,mBAAmB,EAAI,EAG9B,KAAK,MAAM,IAAI,EAAI,CAMnB,QALA,EAAO,MACH,iFACA,EACH,EACD,KAAK,mBAAmB,EAAI,EACrB,KAAK,MAAM,IAAI,EAAI;AAI9B,KAAO,MAAM,iCAAiC,EAAI;GAClD,IAAM,KAAW,YAAY;AACzB,QAAI;AACA,OAAO,MAAM,kCAAkC,EAAI;KACnD,IAAM,IAAS,MAAM,GAAI;AAEzB,YADA,EAAO,MAAM,qCAAqC,EAAI,EAC/C;aACF,GAAO;AAEZ,WADA,EAAO,MAAM,6BAA6B,GAAK,EAAM,EAC/C;cACA;AAGN,KAFA,EAAO,MAAM,0DAA0D,EAAI,EAC3E,KAAK,MAAM,OAAO,EAAI,EACtB,KAAK,mBAAmB,EAAI;;OAEhC;AAIJ,UAFA,KAAK,MAAM,IAAI,GAAK,EAAQ,EAC5B,EAAO,MAAM,iDAAiD,GAAK,KAAK,MAAM,KAAK,EAC5E;;EAUX,MAAc,mBAAmB,GAA4B;AACzD,OAAI,KAAK,eAAe,KAAK,eAAe;AAExC,IADA,KAAK,gBACL,EAAO,MACH,qEACA,GACA,KAAK,cACL,KAAK,eACL,KAAK,UAAU,OAClB;AACD;;AAcJ,UAXA,EAAO,MACH,4DACA,GACA,KAAK,cACL,KAAK,eACL,KAAK,UAAU,OAClB,EAED,EAAO,UAAU,EAAS,OAAO,EAAI,EAG9B,IAAI,SAAe,MAAwB;AAC9C,SAAK,UAAU,WAAW;AAUtB,KATA,EAAO,aAAa,EAAS,OAAO,EAAI,EACxC,KAAK,gBACL,EAAO,MACH,uEACA,GACA,KAAK,cACL,KAAK,eACL,KAAK,UAAU,OAClB,EACD,GAAS;MACX;KACJ;;EAMN,mBAA2B,GAAmB;AAC1C,OAAI,KAAK,UAAU,SAAS,GAAG;AAC3B,MAAO,MACH,8EACA,GACA,KAAK,cACL,KAAK,eACL,KAAK,UAAU,OAClB;IACD,IAAM,IAAO,KAAK,UAAU,OAAO;AACnC,IAAI,KACA,GAAM;SAIV,CADA,KAAK,gBACL,EAAO,MACH,0DACA,GACA,KAAK,cACL,KAAK,eACL,KAAK,UAAU,OAClB;;;;ACnJA,IAjBkC,KACjB,EACxB,IAAiB,EAAa,mBAAmB,EAe1C,IAAb,MAAyB;EAmBrB,YACI,GACA,GACA,GACA,GACF;AACE,mBAhB0B,kBACA,MAetB,KAAW,EACX,OAAU,MAAM,oCAAoC;AASxD,GANA,KAAK,UAAU,GACf,KAAK,YAAY,GACjB,KAAK,eAAe,GACpB,KAAK,YAAY,GACjB,KAAK,wBAAQ,IAAI,KAAK,EACtB,KAAK,2BAAW,IAAI,KAAK,EACzB,KAAK,OAAO,IAAI,EAAiB,EAAQ;;EAO7C,UAAiB,GAAqB;GAClC,IAAM,IAAM,KAAK,UAAU,EAAE,EACvB,IAAa,KAAK,MAAM,IAAI,EAAI;AAEtC,OAAI,EAEA,QADA,KAAK,YAAY,EAAI,EACd;;EAUf,MAAa,IAAI,GAAkB;GAC/B,IAAM,IAAM,KAAK,UAAU,EAAE,EACvB,IAAa,KAAK,MAAM,IAAI,EAAI;AAQtC,UANI,KACA,KAAK,YAAY,EAAI,EACd,MAEX,EAAO,MAAM,WAAW,EAAI,EAErB,KAAK,KAAK,QAAQ,GAAK,YAAY;IACtC,IAAM,IAAW,KAAK,MAAM,IAAI,EAAI;AACpC,QAAI,EAGA,QAFA,EAAO,MAAM,iCAAiC,EAAI,EAClD,KAAK,YAAY,EAAI,EACd;AAIX,IADA,EAAO,KAAK,cAAc,EAAI,EAC9B,EAAO,UAAU,EAAS,MAAM,EAAI;IACpC,IAAM,IAAU,MAAM,KAAK,aAAa,EAAE;AAI1C,WAHA,EAAO,KAAK,aAAa,EAAI,EAC7B,EAAO,aAAa,EAAS,MAAM,EAAI,EACvC,KAAK,IAAI,GAAK,EAAQ,EACf;KACT;;EAMN,QAAqB;AAGjB,OAFA,EAAO,MAAM,QAAQ,EAEjB,KAAK,UACL,MAAK,IAAM,KAAS,KAAK,MAAM,QAAQ,CACnC,MAAK,UAAU,EAAM;AAO7B,GAHA,KAAK,MAAM,OAAO,EAClB,KAAK,SAAS,OAAO,EACrB,KAAK,OAAO,MACZ,KAAK,OAAO;;EAYhB,IAAc,GAAe;GACzB,IAAM,IAAM,KAAK,UAAU,EAAE;AAC7B,UAAO,KAAK,MAAM,IAAI,EAAI;;EAO9B,UAA8B;AAC1B,UAAO,MAAM,KAAK,KAAK,MAAM,MAAM,CAAC;;EAQxC,WAAqB,IAAgB,IAAc;GAC/C,IAAM,IAAmB,EAAE,EACvB,IAAU,KAAK;AAEnB,UAAO,KAAW,EAAO,SAAS,GAG9B,CAFA,EAAO,KAAK,EAAQ,EAEpB,IADa,KAAK,SAAS,IAAI,EACrB,EAAM,QAAQ;AAG5B,UAAO;;EAUX,IAAY,GAAa,GAAgB;AAQrC,GANI,KAAK,MAAM,QAAQ,KAAK,WACxB,KAAK,wBAAwB,EAIjC,KAAK,MAAM,IAAI,GAAK,EAAM,EAC1B,KAAK,WAAW,EAAI;;EAMxB,OAAe,GAAsB;AAEjC,OADA,EAAO,MAAM,aAAa,EAAI,EAC1B,CAAC,KAAK,MAAM,IAAI,EAAI,CACpB,QAAO;GAGX,IAAM,IAAQ,KAAK,MAAM,IAAI,EAAI;AASjC,UARA,KAAK,MAAM,OAAO,EAAI,EACtB,KAAK,cAAc,EAAI,EAGnB,KAAS,KAAK,aACd,KAAK,UAAU,EAAM,EAGlB;;EAUX,yBAAuC;AACnC,OAAI,CAAC,KAAK,KACN;GAGJ,IAAM,IAAS,KAAK;AACpB,QAAK,OAAO,EAAO;;EAUvB,WAAmB,GAAmB;GAClC,IAAM,IAAO;IAAE,MAAM;IAAM,MAAM,KAAK;IAAM;AAG5C,OAFA,KAAK,SAAS,IAAI,GAAK,EAAK,EAExB,KAAK,MAAM;IACX,IAAM,IAAW,KAAK,SAAS,IAAI,KAAK,KAAK;AAC7C,MAAS,OAAO;SAGhB,MAAK,OAAO;AAGhB,QAAK,OAAO;;EAMhB,YAAoB,GAAmB;AAC/B,QAAK,SAAS,MAKlB,KAAK,cAAc,EAAI,EAGvB,KAAK,WAAW,EAAI;;EAMxB,cAAsB,GAAmB;GACrC,IAAM,IAAO,KAAK,SAAS,IAAI,EAAI;AAC9B,UAKL;QAAI,EAAK,MAAM;KACX,IAAM,IAAW,KAAK,SAAS,IAAI,EAAK,KAAK;AAC7C,OAAS,OAAO,EAAK;UAGrB,MAAK,OAAO,EAAK;AAIrB,QAAI,EAAK,MAAM;KACX,IAAM,IAAW,KAAK,SAAS,IAAI,EAAK,KAAK;AAC7C,OAAS,OAAO,EAAK;UAGrB,MAAK,OAAO,EAAK;AAGrB,SAAK,SAAS,OAAO,EAAI;;;;;ICxRX;;ACOT,IALkC,EAGzC,IAAiB,EAAa,0BAA0B,EAEjD,IAAb,MAAwB;EACpB,YACI,GACA,GACF;AADU,GADA,KAAA,kBAAA,GACA,KAAA,cAAA;;EAOZ,MAAa,SAAS,GAA4C;GAC9D,IAAM,IAAU,GAAG,EAAW,EAAE,GAAG,EAAW,EAAE,GAAG,EAAW,KACxD,IAAU,KAAK,WAAW,EAAW,EAErC,IAAa,SAAS;AAC5B,KAAO,UAAU,EAAS,OAAO,EAAW;AAC5C,OAAI;IACA,IAAM,IAAO,MAAM,KAAK,YAAY,UAAU,EAAQ;AAEtD,WADA,EAAO,aAAa,EAAS,OAAO,EAAW,EACxC;YACF,GAAO;AAOZ,UANA,EAAO,aAAa,EAAS,OAAO,EAAW,EAC3C,aAAiB,QACP,MAAM,6BAA6B,EAAQ,IAAI,EAAM,WAAW,EACtE,OAAO,GACV,CAAC,GAEI,MAAM,6BAA6B,EAAQ,kBAAkB,EACnE,OAAO,GACV,CAAC;;;EAQV,WAAmB,GAAqC;GAEpD,IAAM,IAAa,SAAS,GADT,EAAW,EAAE,GAAG,EAAW,EAAE,GAAG,EAAW;AAG9D,UADA,EAAO,UAAU,EAAS,OAAO,EAAW,EACrC,KAAK,gBACP,QAAQ,OAAO,EAAW,EAAE,UAAU,CAAC,CACvC,QAAQ,OAAO,EAAW,EAAE,UAAU,CAAC,CACvC,QAAQ,OAAO,EAAW,EAAE,UAAU,CAAC;;;;ACvCvC,IAZwB,EAE/B,IAAiB,EAAa,0BAA0B,EAUjD,IAAb,MAA2B;EAUvB,YAAY,GAAkB;AAC1B,oBATqB,EAAE,kBACS,sBACG,sBACmB,0BAC3B,wBACC,wBACA,GAG5B,KAAK,UAAU;;EAMnB,UAAoB;AAChB,QAAK;GACL,IAAI,IAAS,KAAK,UAAU,KAAK;AAoBjC,UAlBK,IASD,EAAO,MACH,sEACA,KAAK,UAAU,SAAS,GACxB,KAAK,UAAU,QACf,KAAK,cACR,IAbD,IAAS,KAAK,SAAS,EACvB,KAAK,gBACL,EAAO,MACH,kEACA,KAAK,cACL,KAAK,UAAU,OAClB,GAUL,KAAK,iBAAiB,EACf;;EAMX,QAAe,GAAiB;AAC5B,GAAI,KACA,KAAK,iBACL,KAAK,UAAU,KAAK,EAAO,EAC3B,EAAO,MACH,oEACA,KAAK,UAAU,SAAS,GACxB,KAAK,UAAU,QACf,KAAK,cACR,EACD,KAAK,iBAAiB,IAEtB,EAAO,KAAK,sDAAsD;;EAO1E,kBAAgC;AAO5B,GANI,KAAK,aACL,aAAa,KAAK,UAAU,EAC5B,EAAO,MAAM,4CAA4C,IAEzD,EAAO,MAAM,8CAA8C,KAAK,YAAY,EAEhF,KAAK,YAAY,iBAAiB,KAAK,OAAO,EAAE,KAAK,YAAY;;EAMrE,QAAsB;GAClB,IAAM,IAAc,KAAK,UAAU,QAC/B,IAAU;AAEd,OAAI,IAAc,KAAK,UAAU;AAO7B,SANA,EAAO,MACH,4DACA,GACA,KAAK,SACR,EAEM,KAAK,UAAU,SAAS,KAAK,UAEhC,CADA,KAAK,UAAU,KAAK,EACpB;AAGJ,MAAO,KACH,kEACA,GACA,GACA,KAAK,UAAU,OAClB;SAED,GAAO,MACH,yDACA,GACA,KAAK,SACR;AAIL,QAAK,YAAY;;;;KCvHE;;ACEd,IAFQ,EAER,IAAb,cAA8B,EAAK;EAC/B,YAAY,GAA0B;AAAjB,GACjB,MAAM,EAAK,OAAO,EAAK,OAAO,EADb,KAAA,OAAA;;EAGrB,QAAe;EAQf,oBAA2B,GAAyB;AAChD,UAAO;IACH,KAAK,KAAK,KAAK,KAAK;IACpB,OAAO,KAAK,KAAK,KAAK,IAAQ;IAC9B,MAAM,KAAK,KAAK,KAAK,IAAQ;IAEhC;;;;AChBI,KAJ2B,MAEf,EAEZ,IAAb,MAAsD;EAGlD,cAAc;AACV,QAAK,aAAa,IAAI,QAAyB,EAAa,KAAK,IAAI,CAAC;;EAS1E,MAAa,UAAU,GAA4B;GAC/C,IAAM,IAAQ,MAAM,EAAU,EAAI,EAE5B,IAAS,KAAK,WAAW,SAAS;AAExC,OAAI;AAKA,IAHI,EAAO,UAAU,EAAM,UACvB,EAAO,QAAQ,EAAM,QAErB,EAAO,WAAW,EAAM,WACxB,EAAO,SAAS,EAAM;IAG1B,IAAM,IAAM,EAAO,WAAW,KAAK;AAInC,WAFA,EAAI,UAAU,GAAO,GAAG,EAAE,EAEnB,IAAI,EADE,EAAI,aAAa,GAAG,GAAG,EAAM,OAAO,EAAM,OACnC,CAAK;aACnB;AAEN,SAAK,WAAW,QAAQ,EAAO;;;;;ACjC9B,IANS,KAEK,EAId,IAAb,MAAyB;EAGrB,YACI,GACA,GACF;AADmB,GADA,KAAA,kBAAA,GACA,KAAA,YAAA;;EAGrB,cAAqB,GAA+C;AAChE,OAAI,KAAK,MACL,QAAO,KAAK,MAAM,UAAU,EAAW;;EAK/C,MAAa,QAAQ,GAA4C;AAI7D,UAHI,KAAK,QACE,MAAM,KAAK,MAAM,IAAI,EAAW,GAEpC,QAAQ,OAAO,gBAAI,MAAM,wBAAwB,CAAC;;EAG7D,MAAM,YAAmD;AACrD,OAAK,KAAK,MAuBN,QAAO,KAAK;GAvBC;IACb,IAAI;IACU;KACV,IAAM,EAAE,yBAAsB,MAAA,QAAA,SAAA,CAAA,YAAA,IAAA,EAAA,IAAA;AAC9B,SAAc,IAAI,GAAmB;;IAMzC,IAAM,KAAmB,MAAqB,EAAW,OAAO,EAE1D,IAAa,IAAI,EAAW,KAAK,iBAAiB,EAAY,EAE9D,IAAe,IAAI,EACrB,KAAK,YACL,MAAc,GAAG,EAAW,EAAE,GAAG,EAAW,EAAE,GAAG,EAAW,MAC5D,MAAc,EAAW,SAAS,EAAW,EAC7C,EACH;AAED,WADA,KAAK,QAAQ,GACN;;;;;AChDG,KAAtB,MAA2B;EAEvB,YACI,GACA,GACF;AAEE,GAJS,KAAA,QAAA,GACA,KAAA,SAAA,GAET,KAAK,QAAQ,IAAI,aAAa,IAAQ,EAAO,EAC7C,KAAK,MAAM,KAAK,IAAI;;EAMxB,aAAa,GAAyB;AAElC,OAAI,EAAS,IAAI,KAAK,EAAS,KAAK,KAAK,MACrC,OAAU,MACN,uBAAuB,EAAS,EAAE,0BAA0B,KAAK,QAAQ,IAC5E;AAGL,OAAI,EAAS,IAAI,KAAK,EAAS,KAAK,KAAK,OACrC,OAAU,MACN,uBAAuB,EAAS,EAAE,0BAA0B,KAAK,SAAS,IAC7E;GAIL,IAAM,KAAS,EAAS,IAAI,KAAK,QAAQ,EAAS,KAAK;AACvD,OAAI,MAAM,KAAK,MAAM,GAAO,EAAE;IAC1B,IAAM,IAAM,KAAK,oBAAoB,EAAM,EACrC,IAAY,KAAK,gBAAgB,EAAI;AAE3C,WADA,KAAK,MAAM,KAAS,GACb;SAEP,QAAO,KAAK,MAAM;;EAU1B,gBAAgB,GAAuB;GACnC,IAAM,IAAY,EAAI,MAAM,MAAM,EAAI,QAAQ,EAAI,OAAO,MAAM;AAC/D,UAAO,KAAK,MAAM,IAAY,IAAI,GAAG;;;;KCjDjB,MACP;;GCAO,KACS;AAWrC,IAAM,KAAiB,EAAa,oBAAoB,EAK3C,IAAb,MAA+B;CAU3B,YAAY,IAAkC,EAAE,EAAE;AAiB9C,EAhBA,KAAK,SAAS;GACV,WAAW,EAAO,aAAa;GAC/B,WAAW,EAAO,aAAa;GAC/B,iBACI,EAAO,mBAAmB;GAC9B,UAAU,EAAO,YAAY;GAC7B,aAAa,EAAO,eAAe;IAC/B,MAAM;IACN,KAAK;IACR;GACJ,EACD,GAAO,IAAI,YAAY,KAAK,OAAO,EAEnC,KAAK,gBAAgB,EACrB,KAAK,cAAc,IAAI,EAAY,KAAK,OAAO,iBAAiB,KAAK,OAAO,UAAU,EACtF,KAAK,aAAa,IAAI,EAAoB,KAAK,aAAa,KAAK,OAAO,SAAS,EACjF,KAAK,kBAAkB,IAAI,EAAgB,KAAK,WAAW;;CAM/D,YAAgE;AAC5D,SAAO,EAAE,GAAG,KAAK,QAAQ;;CAM7B,iBAAqC;AACjC,SAAO,KAAK,OAAO;;CAavB,MAAa,aACT,GACA,GACA,GACe;AACf,QAAM,KAAK,YAAY,WAAW;EAClC,IAAM,IAAgB,GAAS,iBAAiB,IAC1C,IAAsB;GAAE;GAAU;GAAW;AACnD,SAAO,MAAM,KAAK,WAAW,aAAa,GAAQ,KAAK,OAAO,WAAW,EAAc;;CAY3F,MAAa,cACT,GACA,GACa;AACb,QAAM,KAAK,YAAY,WAAW;EAClC,IAAM,IAAgB,GAAS,iBAAiB;AAChD,QAAM,KAAK,gBAAgB,cAAc,GAAa,KAAK,OAAO,WAAW,EAAc;;CAQ/F,MAAa,mBACT,GACA,GAC+B;AAC/B,QAAM,KAAK,YAAY,WAAW;EAClC,IAAM,IAAO,GAAS,QAAQ,IACxB,IAAc,GAAS,eAAe,GACtC,IAAgB,GAAS,iBAAiB,IAC1C,IAAmB,GAAS,kBAC5B,IAAgB,GAAS;AAC/B,SAAO,KAAK,gBAAgB,mBACxB,GACA,KAAK,OAAO,WACZ,GACA,GACA,GACA,GACA,EACH;;CAOL,iBAA+B;EAC3B,IAAM,EAAE,cAAW,iBAAc,KAAK;AAEtC,MAAI,CAAC,OAAO,UAAU,EAAU,IAAI,IAAY,KAAK,IAAY,GAC7D,OAAU,MACN,uBAAuB,EAAU,uCACpC;AAGL,MAAI,CAAC,OAAO,UAAU,EAAU,IAAI,KAAa,EAC7C,OAAU,MAAM,uBAAuB,EAAU,8BAA8B;EAGnF,IAAM,EAAE,gBAAa,KAAK;AAC1B,MAAI,CAAC,OAAO,UAAU,EAAS,IAAI,KAAY,KAAM,IAAY,IAAW,EACxE,OAAU,MAAM,sBAAsB,EAAS,iCAAiC"}