@k-msg/analytics 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/services/metrics.collector.ts","../src/types/analytics.types.ts","../src/services/report.generator.ts","../src/services/insight.engine.ts","../src/services/analytics.service.ts","../src/aggregators/time-series.aggregator.ts","../src/aggregators/metric.aggregator.ts","../src/collectors/event.collector.ts","../src/collectors/webhook.collector.ts","../src/insights/anomaly.detector.ts","../src/insights/recommendation.engine.ts","../src/reports/dashboard.ts","../src/reports/export.manager.ts"],"sourcesContent":["/**\n * Analytics Engine\n * 메시지 전송 통계 및 분석 기능 제공\n */\n\n// 핵심 서비스\nexport { AnalyticsService } from './services/analytics.service';\nexport { MetricsCollector } from './services/metrics.collector';\nexport { ReportGenerator } from './services/report.generator';\nexport { InsightEngine } from './services/insight.engine';\n\n// 집계 컴포넌트\nexport { TimeSeriesAggregator, MetricAggregator } from './aggregators/index';\nexport type { TimeWindow, AggregationOptions, AggregationRule, AggregatorConfig } from './aggregators/index';\n\n// 수집 컴포넌트\nexport { EventCollector, WebhookCollector } from './collectors/index';\nexport type { \n EventData, \n EventCollectorConfig, \n EventProcessor,\n WebhookData,\n WebhookCollectorConfig\n} from './collectors/index';\n\n// 인사이트 컴포넌트\nexport { AnomalyDetector, RecommendationEngine } from './insights/index';\nexport type {\n AnomalyDetectionConfig,\n AnomalyAlgorithm,\n Anomaly,\n RecommendationConfig,\n Recommendation,\n RecommendationRule\n} from './insights/index';\n\n// 보고서 컴포넌트\nexport { DashboardGenerator, ExportManager } from './reports/index';\nexport type {\n DashboardConfig,\n DashboardWidget,\n DashboardData,\n KPIData,\n WidgetData,\n ExportConfig,\n ExportFormat,\n ExportResult\n} from './reports/index';\n\n// 타입 정의\nexport type {\n AnalyticsConfig,\n MetricData,\n AnalyticsReport,\n InsightData,\n AggregatedMetric,\n AnalyticsQuery,\n AnalyticsResult,\n ReportMetric\n} from './types/analytics.types';\n\n// Enum 정의\nexport { MetricType } from './types/analytics.types';","import type { AnalyticsConfig, MetricData, MetricType } from '../types/analytics.types';\n\nexport class MetricsCollector {\n private config: AnalyticsConfig;\n private buffer: MetricData[] = [];\n private batchSize = 1000;\n private flushInterval = 5000; // 5초\n private storage: Map<string, MetricData[]> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = config;\n this.startBatchProcessor();\n }\n\n /**\n * 메트릭 수집\n */\n async collect(metric: MetricData): Promise<void> {\n // 메트릭 검증\n this.validateMetric(metric);\n\n // 버퍼에 추가\n this.buffer.push(metric);\n\n // 버퍼가 가득 찬 경우 즉시 플러시\n if (this.buffer.length >= this.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * 여러 메트릭 일괄 수집\n */\n async collectBatch(metrics: MetricData[]): Promise<void> {\n for (const metric of metrics) {\n await this.collect(metric);\n }\n }\n\n /**\n * 최근 메트릭 조회\n */\n async getRecentMetrics(types: MetricType[], durationMs: number): Promise<MetricData[]> {\n const cutoff = new Date(Date.now() - durationMs);\n const recent: MetricData[] = [];\n\n for (const type of types) {\n const typeKey = type.toString();\n const metrics = this.storage.get(typeKey) || [];\n \n const recentMetrics = metrics.filter(m => m.timestamp >= cutoff);\n recent.push(...recentMetrics);\n }\n\n return recent.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());\n }\n\n /**\n * 메트릭 통계 조회\n */\n async getMetricStats(type: MetricType, timeRange: { start: Date; end: Date }) {\n const typeKey = type.toString();\n const metrics = this.storage.get(typeKey) || [];\n \n const filtered = metrics.filter(m => \n m.timestamp >= timeRange.start && m.timestamp <= timeRange.end\n );\n\n if (filtered.length === 0) {\n return {\n count: 0,\n sum: 0,\n avg: 0,\n min: 0,\n max: 0,\n };\n }\n\n const values = filtered.map(m => m.value);\n const sum = values.reduce((acc, val) => acc + val, 0);\n\n return {\n count: filtered.length,\n sum,\n avg: sum / filtered.length,\n min: Math.min(...values),\n max: Math.max(...values),\n };\n }\n\n /**\n * 메트릭 카운터 증가\n */\n async incrementCounter(type: MetricType, dimensions: Record<string, string>, value = 1): Promise<void> {\n const metric: MetricData = {\n id: this.generateMetricId(),\n type,\n timestamp: new Date(),\n value,\n dimensions,\n };\n\n await this.collect(metric);\n }\n\n /**\n * 메트릭 게이지 값 설정\n */\n async setGauge(type: MetricType, dimensions: Record<string, string>, value: number): Promise<void> {\n const metric: MetricData = {\n id: this.generateMetricId(),\n type,\n timestamp: new Date(),\n value,\n dimensions,\n };\n\n await this.collect(metric);\n }\n\n /**\n * 메트릭 히스토그램 기록\n */\n async recordHistogram(type: MetricType, dimensions: Record<string, string>, value: number): Promise<void> {\n const metric: MetricData = {\n id: this.generateMetricId(),\n type,\n timestamp: new Date(),\n value,\n dimensions,\n metadata: {\n metricClass: 'histogram',\n },\n };\n\n await this.collect(metric);\n }\n\n /**\n * 메트릭 버퍼 플러시\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) {\n return;\n }\n\n const metrics = [...this.buffer];\n this.buffer = [];\n\n try {\n await this.persistMetrics(metrics);\n } catch (error) {\n console.error('Failed to persist metrics:', error);\n // 실패한 메트릭을 다시 버퍼에 추가 (재시도 로직)\n this.buffer.unshift(...metrics);\n throw error;\n }\n }\n\n /**\n * 메트릭 정리 (보존 기간 초과)\n */\n async cleanup(): Promise<void> {\n const cutoff = new Date(Date.now() - (this.config.retentionDays * 24 * 60 * 60 * 1000));\n \n for (const [typeKey, metrics] of this.storage.entries()) {\n const filtered = metrics.filter(m => m.timestamp >= cutoff);\n this.storage.set(typeKey, filtered);\n }\n }\n\n private validateMetric(metric: MetricData): void {\n if (!metric.id) {\n throw new Error('Metric ID is required');\n }\n\n if (!metric.type) {\n throw new Error('Metric type is required');\n }\n\n if (typeof metric.value !== 'number' || isNaN(metric.value)) {\n throw new Error('Metric value must be a valid number');\n }\n\n if (!metric.timestamp || !(metric.timestamp instanceof Date)) {\n throw new Error('Invalid metric timestamp');\n }\n\n if (!metric.dimensions || typeof metric.dimensions !== 'object') {\n throw new Error('Metric dimensions must be an object');\n }\n }\n\n private async persistMetrics(metrics: MetricData[]): Promise<void> {\n // 메트릭 타입별로 그룹화\n const grouped = new Map<string, MetricData[]>();\n \n for (const metric of metrics) {\n const typeKey = metric.type.toString();\n if (!grouped.has(typeKey)) {\n grouped.set(typeKey, []);\n }\n grouped.get(typeKey)!.push(metric);\n }\n\n // 각 타입별로 저장\n for (const [typeKey, typeMetrics] of grouped.entries()) {\n const existing = this.storage.get(typeKey) || [];\n existing.push(...typeMetrics);\n \n // 최신 순으로 정렬\n existing.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());\n \n // 메모리 사용량 제한 (최근 10,000개만 유지)\n if (existing.length > 10000) {\n existing.splice(10000);\n }\n \n this.storage.set(typeKey, existing);\n }\n\n console.log(`Persisted ${metrics.length} metrics`);\n }\n\n private startBatchProcessor(): void {\n setInterval(async () => {\n try {\n await this.flush();\n } catch (error) {\n console.error('Batch processing failed:', error);\n }\n }, this.flushInterval);\n\n // 정리 작업 (매일 1회)\n setInterval(async () => {\n try {\n await this.cleanup();\n } catch (error) {\n console.error('Cleanup failed:', error);\n }\n }, 24 * 60 * 60 * 1000);\n }\n\n private generateMetricId(): string {\n return `metric_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n}","import { z } from 'zod';\n\nexport interface AnalyticsConfig {\n enableRealTimeTracking: boolean;\n retentionDays: number;\n aggregationIntervals: ('minute' | 'hour' | 'day' | 'week' | 'month')[];\n enabledMetrics: MetricType[];\n}\n\nexport enum MetricType {\n MESSAGE_SENT = 'message_sent',\n MESSAGE_DELIVERED = 'message_delivered',\n MESSAGE_FAILED = 'message_failed',\n MESSAGE_CLICKED = 'message_clicked',\n TEMPLATE_USAGE = 'template_usage',\n PROVIDER_PERFORMANCE = 'provider_performance',\n CHANNEL_USAGE = 'channel_usage',\n ERROR_RATE = 'error_rate',\n DELIVERY_RATE = 'delivery_rate',\n CLICK_RATE = 'click_rate'\n}\n\nexport interface MetricData {\n id: string;\n type: MetricType;\n timestamp: Date;\n value: number;\n dimensions: Record<string, string>; // provider, channel, template_id 등\n metadata?: Record<string, any>;\n}\n\nexport interface AggregatedMetric {\n type: MetricType;\n interval: 'minute' | 'hour' | 'day' | 'week' | 'month';\n timestamp: Date;\n dimensions: Record<string, string>;\n aggregations: {\n count: number;\n sum: number;\n avg: number;\n min: number;\n max: number;\n };\n}\n\nexport interface AnalyticsReport {\n id: string;\n name: string;\n description?: string;\n dateRange: {\n start: Date;\n end: Date;\n };\n filters: Record<string, any>;\n metrics: ReportMetric[];\n generatedAt: Date;\n format: 'json' | 'csv' | 'pdf';\n}\n\nexport interface ReportMetric {\n type: MetricType;\n value: number;\n change?: number; // 이전 기간 대비 변화율\n trend?: 'up' | 'down' | 'stable';\n breakdown?: Record<string, number>; // 세부 분석\n}\n\nexport interface InsightData {\n id: string;\n type: 'anomaly' | 'trend' | 'recommendation';\n title: string;\n description: string;\n severity: 'low' | 'medium' | 'high' | 'critical';\n metric: MetricType;\n dimensions: Record<string, string>;\n value: number;\n expectedValue?: number;\n confidence: number; // 0-1\n actionable: boolean;\n recommendations?: string[];\n detectedAt: Date;\n}\n\nexport interface AnalyticsQuery {\n metrics: MetricType[];\n dateRange: {\n start: Date;\n end: Date;\n };\n interval?: 'minute' | 'hour' | 'day' | 'week' | 'month';\n filters?: Record<string, any>;\n groupBy?: string[];\n orderBy?: { field: string; direction: 'asc' | 'desc' }[];\n limit?: number;\n offset?: number;\n}\n\nexport interface AnalyticsResult {\n query: AnalyticsQuery;\n data: AggregatedMetric[];\n summary: {\n totalRecords: number;\n dateRange: { start: Date; end: Date };\n executionTime: number; // ms\n };\n insights?: InsightData[];\n}\n\n// Zod Schemas\nexport const MetricDataSchema = z.object({\n id: z.string(),\n type: z.nativeEnum(MetricType),\n timestamp: z.date(),\n value: z.number(),\n dimensions: z.record(z.string(), z.string()),\n metadata: z.record(z.string(), z.any()).optional(),\n});\n\nexport const AnalyticsQuerySchema = z.object({\n metrics: z.array(z.nativeEnum(MetricType)),\n dateRange: z.object({\n start: z.date(),\n end: z.date(),\n }),\n interval: z.enum(['minute', 'hour', 'day', 'week', 'month']).optional(),\n filters: z.record(z.string(), z.any()).optional(),\n groupBy: z.array(z.string()).optional(),\n orderBy: z.array(z.object({\n field: z.string(),\n direction: z.enum(['asc', 'desc']),\n })).optional(),\n limit: z.number().min(1).max(10000).optional(),\n offset: z.number().min(0).optional(),\n});\n\nexport const InsightDataSchema = z.object({\n id: z.string(),\n type: z.enum(['anomaly', 'trend', 'recommendation']),\n title: z.string(),\n description: z.string(),\n severity: z.enum(['low', 'medium', 'high', 'critical']),\n metric: z.nativeEnum(MetricType),\n dimensions: z.record(z.string(), z.string()),\n value: z.number(),\n expectedValue: z.number().optional(),\n confidence: z.number().min(0).max(1),\n actionable: z.boolean(),\n recommendations: z.array(z.string()).optional(),\n detectedAt: z.date(),\n});\n\nexport type MetricDataType = z.infer<typeof MetricDataSchema>;\nexport type AnalyticsQueryType = z.infer<typeof AnalyticsQuerySchema>;\nexport type InsightDataType = z.infer<typeof InsightDataSchema>;","import type { \n AnalyticsConfig, \n AnalyticsReport, \n ReportMetric,\n AggregatedMetric \n} from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport class ReportGenerator {\n private config: AnalyticsConfig;\n\n constructor(config: AnalyticsConfig) {\n this.config = config;\n }\n\n /**\n * 일일 요약 보고서 생성\n */\n async generateDailySummary(date: Date): Promise<AnalyticsReport> {\n const startOfDay = new Date(date);\n startOfDay.setHours(0, 0, 0, 0);\n \n const endOfDay = new Date(date);\n endOfDay.setHours(23, 59, 59, 999);\n\n const previousDay = new Date(startOfDay);\n previousDay.setDate(previousDay.getDate() - 1);\n\n return this.generateReport({\n id: `daily_${date.toISOString().split('T')[0]}`,\n name: `Daily Summary - ${date.toISOString().split('T')[0]}`,\n description: 'Daily messaging performance summary',\n dateRange: { start: startOfDay, end: endOfDay },\n filters: {},\n metrics: await this.calculateDailyMetrics(startOfDay, endOfDay, previousDay),\n generatedAt: new Date(),\n format: 'json',\n });\n }\n\n /**\n * 주간 보고서 생성\n */\n async generateWeeklyReport(weekStartDate: Date): Promise<AnalyticsReport> {\n const weekEnd = new Date(weekStartDate);\n weekEnd.setDate(weekEnd.getDate() + 6);\n\n const previousWeekStart = new Date(weekStartDate);\n previousWeekStart.setDate(previousWeekStart.getDate() - 7);\n\n return this.generateReport({\n id: `weekly_${weekStartDate.toISOString().split('T')[0]}`,\n name: `Weekly Report - Week of ${weekStartDate.toISOString().split('T')[0]}`,\n description: 'Weekly messaging performance analysis',\n dateRange: { start: weekStartDate, end: weekEnd },\n filters: {},\n metrics: await this.calculateWeeklyMetrics(weekStartDate, weekEnd, previousWeekStart),\n generatedAt: new Date(),\n format: 'json',\n });\n }\n\n /**\n * 월간 보고서 생성\n */\n async generateMonthlyReport(year: number, month: number): Promise<AnalyticsReport> {\n const monthStart = new Date(year, month - 1, 1);\n const monthEnd = new Date(year, month, 0);\n \n const previousMonthStart = new Date(year, month - 2, 1);\n const previousMonthEnd = new Date(year, month - 1, 0);\n\n return this.generateReport({\n id: `monthly_${year}_${month.toString().padStart(2, '0')}`,\n name: `Monthly Report - ${year}-${month.toString().padStart(2, '0')}`,\n description: 'Monthly messaging performance analysis',\n dateRange: { start: monthStart, end: monthEnd },\n filters: {},\n metrics: await this.calculateMonthlyMetrics(monthStart, monthEnd, previousMonthStart, previousMonthEnd),\n generatedAt: new Date(),\n format: 'json',\n });\n }\n\n /**\n * 프로바이더별 성능 보고서\n */\n async generateProviderReport(providerId: string, dateRange: { start: Date; end: Date }): Promise<AnalyticsReport> {\n return this.generateReport({\n id: `provider_${providerId}_${dateRange.start.toISOString().split('T')[0]}`,\n name: `Provider Performance - ${providerId}`,\n description: `Performance analysis for provider ${providerId}`,\n dateRange,\n filters: { provider: providerId },\n metrics: await this.calculateProviderMetrics(providerId, dateRange),\n generatedAt: new Date(),\n format: 'json',\n });\n }\n\n /**\n * 템플릿 사용량 보고서\n */\n async generateTemplateUsageReport(dateRange: { start: Date; end: Date }): Promise<AnalyticsReport> {\n return this.generateReport({\n id: `template_usage_${dateRange.start.toISOString().split('T')[0]}`,\n name: 'Template Usage Report',\n description: 'Analysis of template usage and performance',\n dateRange,\n filters: {},\n metrics: await this.calculateTemplateMetrics(dateRange),\n generatedAt: new Date(),\n format: 'json',\n });\n }\n\n /**\n * 커스텀 보고서 생성\n */\n async generateCustomReport(\n name: string,\n dateRange: { start: Date; end: Date },\n filters: Record<string, any>,\n metricTypes: MetricType[]\n ): Promise<AnalyticsReport> {\n return this.generateReport({\n id: `custom_${Date.now()}`,\n name,\n description: 'Custom analytics report',\n dateRange,\n filters,\n metrics: await this.calculateCustomMetrics(dateRange, filters, metricTypes),\n generatedAt: new Date(),\n format: 'json',\n });\n }\n\n /**\n * 보고서를 CSV 형식으로 내보내기\n */\n async exportToCSV(report: AnalyticsReport): Promise<string> {\n const headers = ['Metric Type', 'Value', 'Change (%)', 'Trend'];\n const rows = report.metrics.map(metric => [\n metric.type.toString(),\n metric.value.toString(),\n metric.change?.toFixed(2) || '0',\n metric.trend || 'stable',\n ]);\n\n const csvContent = [headers, ...rows]\n .map(row => row.map(cell => `\"${cell}\"`).join(','))\n .join('\\n');\n\n return csvContent;\n }\n\n /**\n * 보고서를 JSON 형식으로 내보내기\n */\n async exportToJSON(report: AnalyticsReport): Promise<string> {\n return JSON.stringify(report, null, 2);\n }\n\n private async generateReport(reportData: AnalyticsReport): Promise<AnalyticsReport> {\n // 보고서 유효성 검사\n this.validateReport(reportData);\n\n // 메트릭 정렬 (중요도 순)\n reportData.metrics.sort((a, b) => {\n const priority = this.getMetricPriority(a.type) - this.getMetricPriority(b.type);\n return priority;\n });\n\n return reportData;\n }\n\n private async calculateDailyMetrics(\n startDate: Date, \n endDate: Date, \n previousDate: Date\n ): Promise<ReportMetric[]> {\n const metrics: ReportMetric[] = [];\n\n // 주요 KPI 계산\n const totalSent = await this.getMetricValue(MetricType.MESSAGE_SENT, startDate, endDate);\n const totalDelivered = await this.getMetricValue(MetricType.MESSAGE_DELIVERED, startDate, endDate);\n const totalFailed = await this.getMetricValue(MetricType.MESSAGE_FAILED, startDate, endDate);\n const totalClicked = await this.getMetricValue(MetricType.MESSAGE_CLICKED, startDate, endDate);\n\n // 이전 기간 데이터\n const previousStart = new Date(previousDate);\n const previousEnd = new Date(previousDate);\n previousEnd.setHours(23, 59, 59, 999);\n\n const prevSent = await this.getMetricValue(MetricType.MESSAGE_SENT, previousStart, previousEnd);\n \n metrics.push({\n type: MetricType.MESSAGE_SENT,\n value: totalSent,\n change: this.calculateChange(totalSent, prevSent),\n trend: this.calculateTrend(totalSent, prevSent),\n });\n\n if (totalSent > 0) {\n const deliveryRate = (totalDelivered / totalSent) * 100;\n const errorRate = (totalFailed / totalSent) * 100;\n \n metrics.push({\n type: MetricType.DELIVERY_RATE,\n value: deliveryRate,\n change: 0, // TODO: 이전 기간과 비교\n trend: 'stable',\n });\n\n metrics.push({\n type: MetricType.ERROR_RATE,\n value: errorRate,\n change: 0, // TODO: 이전 기간과 비교\n trend: 'stable',\n });\n }\n\n if (totalDelivered > 0) {\n const clickRate = (totalClicked / totalDelivered) * 100;\n \n metrics.push({\n type: MetricType.CLICK_RATE,\n value: clickRate,\n change: 0, // TODO: 이전 기간과 비교\n trend: 'stable',\n });\n }\n\n return metrics;\n }\n\n private async calculateWeeklyMetrics(\n weekStart: Date,\n weekEnd: Date,\n previousWeekStart: Date\n ): Promise<ReportMetric[]> {\n // 주간 메트릭 계산 로직\n return this.calculateDailyMetrics(weekStart, weekEnd, previousWeekStart);\n }\n\n private async calculateMonthlyMetrics(\n monthStart: Date,\n monthEnd: Date,\n previousMonthStart: Date,\n previousMonthEnd: Date\n ): Promise<ReportMetric[]> {\n // 월간 메트릭 계산 로직\n return this.calculateDailyMetrics(monthStart, monthEnd, previousMonthStart);\n }\n\n private async calculateProviderMetrics(\n providerId: string,\n dateRange: { start: Date; end: Date }\n ): Promise<ReportMetric[]> {\n const metrics: ReportMetric[] = [];\n\n // 프로바이더별 성능 메트릭 계산\n const performance = await this.getProviderPerformance(providerId, dateRange);\n \n metrics.push({\n type: MetricType.PROVIDER_PERFORMANCE,\n value: performance.averageResponseTime,\n breakdown: {\n 'Success Rate': performance.successRate,\n 'Error Rate': performance.errorRate,\n 'Avg Response Time': performance.averageResponseTime,\n },\n });\n\n return metrics;\n }\n\n private async calculateTemplateMetrics(\n dateRange: { start: Date; end: Date }\n ): Promise<ReportMetric[]> {\n const metrics: ReportMetric[] = [];\n\n // 템플릿 사용량 분석\n const templateUsage = await this.getTemplateUsage(dateRange);\n \n metrics.push({\n type: MetricType.TEMPLATE_USAGE,\n value: templateUsage.totalUsage,\n breakdown: templateUsage.byTemplate,\n });\n\n return metrics;\n }\n\n private async calculateCustomMetrics(\n dateRange: { start: Date; end: Date },\n filters: Record<string, any>,\n metricTypes: MetricType[]\n ): Promise<ReportMetric[]> {\n const metrics: ReportMetric[] = [];\n\n for (const type of metricTypes) {\n const value = await this.getMetricValue(type, dateRange.start, dateRange.end, filters);\n \n metrics.push({\n type,\n value,\n change: 0, // TODO: 이전 기간과 비교\n trend: 'stable',\n });\n }\n\n return metrics;\n }\n\n private async getMetricValue(\n type: MetricType,\n start: Date,\n end: Date,\n filters?: Record<string, any>\n ): Promise<number> {\n // 실제 구현에서는 데이터베이스 쿼리\n // 여기서는 임시 값 반환\n return Math.floor(Math.random() * 10000);\n }\n\n private async getProviderPerformance(providerId: string, dateRange: { start: Date; end: Date }) {\n return {\n successRate: 95.5,\n errorRate: 4.5,\n averageResponseTime: 250, // ms\n };\n }\n\n private async getTemplateUsage(dateRange: { start: Date; end: Date }) {\n return {\n totalUsage: 5000,\n byTemplate: {\n 'auth_otp': 2000,\n 'welcome': 1500,\n 'notification': 1000,\n 'others': 500,\n },\n };\n }\n\n private calculateChange(current: number, previous: number): number {\n if (previous === 0) return current > 0 ? 100 : 0;\n return ((current - previous) / previous) * 100;\n }\n\n private calculateTrend(current: number, previous: number): 'up' | 'down' | 'stable' {\n const change = this.calculateChange(current, previous);\n if (Math.abs(change) < 5) return 'stable';\n return change > 0 ? 'up' : 'down';\n }\n\n private getMetricPriority(type: MetricType): number {\n const priorities = {\n [MetricType.MESSAGE_SENT]: 1,\n [MetricType.DELIVERY_RATE]: 2,\n [MetricType.ERROR_RATE]: 3,\n [MetricType.CLICK_RATE]: 4,\n [MetricType.MESSAGE_DELIVERED]: 5,\n [MetricType.MESSAGE_FAILED]: 6,\n [MetricType.MESSAGE_CLICKED]: 7,\n [MetricType.TEMPLATE_USAGE]: 8,\n [MetricType.PROVIDER_PERFORMANCE]: 9,\n [MetricType.CHANNEL_USAGE]: 10,\n };\n\n return priorities[type] || 99;\n }\n\n private validateReport(report: AnalyticsReport): void {\n if (!report.id) {\n throw new Error('Report ID is required');\n }\n\n if (!report.name) {\n throw new Error('Report name is required');\n }\n\n if (!report.dateRange || !report.dateRange.start || !report.dateRange.end) {\n throw new Error('Valid date range is required');\n }\n\n if (report.dateRange.start >= report.dateRange.end) {\n throw new Error('Invalid date range: start must be before end');\n }\n }\n}","import type {\n AnalyticsConfig,\n MetricData,\n InsightData,\n AggregatedMetric,\n AnalyticsQuery\n} from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport class InsightEngine {\n private config: AnalyticsConfig;\n private anomalyThresholds: Map<MetricType, { min: number; max: number; stdDev: number }> = new Map();\n private historicalData: Map<MetricType, number[]> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = config;\n this.initializeBaselines();\n }\n\n /**\n * 실시간 이상 탐지\n */\n async detectRealTimeAnomalies(metric: MetricData): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n const threshold = this.anomalyThresholds.get(metric.type);\n\n if (!threshold) {\n return insights;\n }\n\n // 통계적 이상 탐지\n if (metric.value > threshold.max || metric.value < threshold.min) {\n insights.push({\n id: `anomaly_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n type: 'anomaly',\n title: `${metric.type} Anomaly Detected`,\n description: `Value ${metric.value} is outside normal range [${threshold.min}, ${threshold.max}]`,\n severity: this.calculateSeverity(metric.value, threshold),\n metric: metric.type,\n dimensions: metric.dimensions,\n value: metric.value,\n expectedValue: (threshold.min + threshold.max) / 2,\n confidence: this.calculateConfidence(metric.value, threshold),\n actionable: true,\n recommendations: this.generateRecommendations(metric),\n detectedAt: new Date(),\n });\n }\n\n // 급격한 변화 탐지\n const recentTrend = await this.detectTrendChange(metric);\n if (recentTrend) {\n insights.push(recentTrend);\n }\n\n return insights;\n }\n\n /**\n * 시계열 이상 탐지\n */\n async detectAnomalies(metricType: MetricType, timeRange: { start: Date; end: Date }): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n\n // 계절성 이상 탐지\n const seasonalAnomalies = await this.detectSeasonalAnomalies(metricType, timeRange);\n insights.push(...seasonalAnomalies);\n\n // 패턴 이상 탐지\n const patternAnomalies = await this.detectPatternAnomalies(metricType, timeRange);\n insights.push(...patternAnomalies);\n\n // 임계값 기반 이상 탐지\n const thresholdAnomalies = await this.detectThresholdAnomalies(metricType, timeRange);\n insights.push(...thresholdAnomalies);\n\n return insights;\n }\n\n /**\n * 인사이트 생성\n */\n async generateInsights(query: AnalyticsQuery, data: AggregatedMetric[]): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n\n // 성능 인사이트\n const performanceInsights = await this.generatePerformanceInsights(data);\n insights.push(...performanceInsights);\n\n // 트렌드 인사이트\n const trendInsights = await this.generateTrendInsights(data);\n insights.push(...trendInsights);\n\n // 비교 인사이트\n const comparisonInsights = await this.generateComparisonInsights(data);\n insights.push(...comparisonInsights);\n\n // 추천 인사이트\n const recommendationInsights = await this.generateRecommendationInsights(data);\n insights.push(...recommendationInsights);\n\n return insights.sort((a, b) => {\n // 심각도 순으로 정렬\n const severityOrder = { critical: 4, high: 3, medium: 2, low: 1 };\n return severityOrder[b.severity] - severityOrder[a.severity];\n });\n }\n\n /**\n * 트렌드 예측\n */\n async predictTrends(metricType: MetricType, forecastDays: number): Promise<{ date: Date; predicted: number; confidence: number }[]> {\n // 간단한 선형 회귀 기반 예측\n const historical = this.historicalData.get(metricType) || [];\n if (historical.length < 7) {\n return []; // 최소 7일 데이터 필요\n }\n\n const predictions = [];\n const lastValue = historical[historical.length - 1];\n const trend = this.calculateTrend(historical);\n\n for (let i = 1; i <= forecastDays; i++) {\n const predictedValue = lastValue + (trend * i);\n const confidence = Math.max(0.1, 1 - (i * 0.1)); // 시간이 지날수록 신뢰도 감소\n\n predictions.push({\n date: new Date(Date.now() + (i * 24 * 60 * 60 * 1000)),\n predicted: Math.max(0, predictedValue),\n confidence,\n });\n }\n\n return predictions;\n }\n\n private async initializeBaselines(): Promise<void> {\n // 초기 임계값 설정 (실제로는 과거 데이터 기반으로 계산)\n this.anomalyThresholds.set(MetricType.MESSAGE_SENT, { min: 0, max: 10000, stdDev: 500 });\n this.anomalyThresholds.set(MetricType.MESSAGE_DELIVERED, { min: 0, max: 10000, stdDev: 500 });\n this.anomalyThresholds.set(MetricType.MESSAGE_FAILED, { min: 0, max: 1000, stdDev: 100 });\n this.anomalyThresholds.set(MetricType.DELIVERY_RATE, { min: 85, max: 99, stdDev: 5 });\n this.anomalyThresholds.set(MetricType.ERROR_RATE, { min: 0, max: 15, stdDev: 3 });\n }\n\n private async detectTrendChange(metric: MetricData): Promise<InsightData | null> {\n const history = this.historicalData.get(metric.type) || [];\n history.push(metric.value);\n\n // 최근 5개 데이터만 유지\n if (history.length > 5) {\n history.shift();\n }\n\n this.historicalData.set(metric.type, history);\n\n if (history.length < 3) {\n return null; // 트렌드 분석에 충분한 데이터 없음\n }\n\n // 급격한 증가/감소 탐지\n const recentChange = (history[history.length - 1] - history[history.length - 2]) / history[history.length - 2];\n \n if (Math.abs(recentChange) > 0.5) { // 50% 이상 변화\n return {\n id: `trend_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n type: 'trend',\n title: `Sudden ${recentChange > 0 ? 'Increase' : 'Decrease'} in ${metric.type}`,\n description: `${metric.type} changed by ${(recentChange * 100).toFixed(1)}% in the last period`,\n severity: Math.abs(recentChange) > 0.8 ? 'high' : 'medium',\n metric: metric.type,\n dimensions: metric.dimensions,\n value: metric.value,\n expectedValue: history[history.length - 2],\n confidence: 0.8,\n actionable: true,\n recommendations: [`Monitor ${metric.type} closely`, 'Check for system changes or external factors'],\n detectedAt: new Date(),\n };\n }\n\n return null;\n }\n\n private async detectSeasonalAnomalies(metricType: MetricType, timeRange: { start: Date; end: Date }): Promise<InsightData[]> {\n // 계절성 패턴 분석 (요일별, 시간대별 등)\n const insights: InsightData[] = [];\n\n // 예시: 주말에 비해 평일 트래픽이 비정상적으로 낮은 경우\n const isWeekend = timeRange.start.getDay() === 0 || timeRange.start.getDay() === 6;\n const currentHour = timeRange.start.getHours();\n\n // 비즈니스 시간 외 높은 트래픽 탐지\n if (!isWeekend && (currentHour < 6 || currentHour > 22)) {\n // 실제로는 해당 시간대의 메트릭 값을 확인\n insights.push({\n id: `seasonal_${Date.now()}`,\n type: 'anomaly',\n title: 'Unusual Activity During Off-Hours',\n description: `High ${metricType} activity detected during off-business hours`,\n severity: 'medium',\n metric: metricType,\n dimensions: { timeframe: 'off_hours' },\n value: 0, // 실제 값으로 대체\n confidence: 0.7,\n actionable: true,\n recommendations: ['Check for automated systems or bulk operations', 'Verify if this is expected behavior'],\n detectedAt: new Date(),\n });\n }\n\n return insights;\n }\n\n private async detectPatternAnomalies(metricType: MetricType, timeRange: { start: Date; end: Date }): Promise<InsightData[]> {\n // 패턴 변화 탐지 (예: 평소와 다른 발송 패턴)\n return [];\n }\n\n private async detectThresholdAnomalies(metricType: MetricType, timeRange: { start: Date; end: Date }): Promise<InsightData[]> {\n // 정적 임계값 기반 이상 탐지\n return [];\n }\n\n private async generatePerformanceInsights(data: AggregatedMetric[]): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n\n // 배송률이 낮은 경우\n const deliveryMetrics = data.filter(d => d.type === MetricType.DELIVERY_RATE);\n for (const metric of deliveryMetrics) {\n if (metric.aggregations.avg < 90) {\n insights.push({\n id: `performance_delivery_${Date.now()}`,\n type: 'recommendation',\n title: 'Low Delivery Rate Detected',\n description: `Delivery rate of ${metric.aggregations.avg.toFixed(1)}% is below optimal threshold`,\n severity: metric.aggregations.avg < 80 ? 'high' : 'medium',\n metric: MetricType.DELIVERY_RATE,\n dimensions: metric.dimensions,\n value: metric.aggregations.avg,\n expectedValue: 95,\n confidence: 0.9,\n actionable: true,\n recommendations: [\n 'Check provider API status',\n 'Review template approval status',\n 'Verify recipient phone numbers',\n 'Consider switching to backup provider'\n ],\n detectedAt: new Date(),\n });\n }\n }\n\n return insights;\n }\n\n private async generateTrendInsights(data: AggregatedMetric[]): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n\n // 증가/감소 트렌드 분석\n const sentMetrics = data.filter(d => d.type === MetricType.MESSAGE_SENT).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n \n if (sentMetrics.length >= 3) {\n const trend = this.calculateTrend(sentMetrics.map(m => m.aggregations.sum));\n \n if (Math.abs(trend) > 100) { // 일일 100개 이상 변화\n insights.push({\n id: `trend_sent_${Date.now()}`,\n type: 'trend',\n title: `${trend > 0 ? 'Increasing' : 'Decreasing'} Message Volume`,\n description: `Message volume is ${trend > 0 ? 'increasing' : 'decreasing'} by approximately ${Math.abs(trend).toFixed(0)} messages per day`,\n severity: 'low',\n metric: MetricType.MESSAGE_SENT,\n dimensions: {},\n value: sentMetrics[sentMetrics.length - 1].aggregations.sum,\n confidence: 0.7,\n actionable: trend < -500, // 급격한 감소시에만 액션 필요\n recommendations: trend < -500 ? [\n 'Investigate cause of volume decrease',\n 'Check for system issues or campaign changes'\n ] : undefined,\n detectedAt: new Date(),\n });\n }\n }\n\n return insights;\n }\n\n private async generateComparisonInsights(data: AggregatedMetric[]): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n\n // 프로바이더별 성능 비교\n const providerPerformance = new Map<string, { delivered: number; sent: number }>();\n \n for (const metric of data) {\n const provider = metric.dimensions.provider;\n if (!provider) continue;\n\n if (!providerPerformance.has(provider)) {\n providerPerformance.set(provider, { delivered: 0, sent: 0 });\n }\n\n const stats = providerPerformance.get(provider)!;\n if (metric.type === MetricType.MESSAGE_SENT) {\n stats.sent += metric.aggregations.sum;\n } else if (metric.type === MetricType.MESSAGE_DELIVERED) {\n stats.delivered += metric.aggregations.sum;\n }\n }\n\n // 프로바이더별 성능 차이 분석\n const providers = Array.from(providerPerformance.entries());\n if (providers.length > 1) {\n const rates = providers.map(([provider, stats]) => ({\n provider,\n rate: stats.sent > 0 ? (stats.delivered / stats.sent) * 100 : 0,\n volume: stats.sent\n }));\n\n const avgRate = rates.reduce((sum, r) => sum + r.rate, 0) / rates.length;\n const underperforming = rates.filter(r => r.rate < avgRate - 10); // 평균보다 10% 낮은 프로바이더\n\n for (const provider of underperforming) {\n insights.push({\n id: `comparison_provider_${provider.provider}_${Date.now()}`,\n type: 'recommendation',\n title: `Provider ${provider.provider} Underperforming`,\n description: `${provider.provider} delivery rate (${provider.rate.toFixed(1)}%) is significantly below average (${avgRate.toFixed(1)}%)`,\n severity: provider.rate < avgRate - 20 ? 'high' : 'medium',\n metric: MetricType.PROVIDER_PERFORMANCE,\n dimensions: { provider: provider.provider },\n value: provider.rate,\n expectedValue: avgRate,\n confidence: 0.8,\n actionable: true,\n recommendations: [\n `Contact ${provider.provider} support team`,\n 'Consider reducing traffic to this provider',\n 'Check provider-specific settings and configurations'\n ],\n detectedAt: new Date(),\n });\n }\n }\n\n return insights;\n }\n\n private async generateRecommendationInsights(data: AggregatedMetric[]): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n\n // 비용 최적화 추천\n const channelUsage = new Map<string, number>();\n for (const metric of data) {\n if (metric.type === MetricType.CHANNEL_USAGE) {\n const channel = metric.dimensions.channel;\n if (channel) {\n channelUsage.set(channel, (channelUsage.get(channel) || 0) + metric.aggregations.sum);\n }\n }\n }\n\n const totalUsage = Array.from(channelUsage.values()).reduce((sum, usage) => sum + usage, 0);\n if (totalUsage > 0) {\n const smsUsage = channelUsage.get('sms') || 0;\n const alimtalkUsage = channelUsage.get('alimtalk') || 0;\n\n // SMS 사용률이 높은 경우 알림톡 전환 추천\n if (smsUsage / totalUsage > 0.3 && alimtalkUsage > 0) {\n insights.push({\n id: `recommendation_channel_${Date.now()}`,\n type: 'recommendation',\n title: 'Consider Switching to AlimTalk',\n description: `${((smsUsage / totalUsage) * 100).toFixed(1)}% of messages are sent via SMS. AlimTalk could reduce costs`,\n severity: 'low',\n metric: MetricType.CHANNEL_USAGE,\n dimensions: { optimization: 'cost' },\n value: smsUsage,\n confidence: 0.8,\n actionable: true,\n recommendations: [\n 'Evaluate message types suitable for AlimTalk conversion',\n 'Create AlimTalk templates for common SMS use cases',\n 'Implement fallback logic for failed AlimTalk messages'\n ],\n detectedAt: new Date(),\n });\n }\n }\n\n return insights;\n }\n\n private calculateSeverity(value: number, threshold: { min: number; max: number; stdDev: number }): 'low' | 'medium' | 'high' | 'critical' {\n const distance = Math.min(\n Math.abs(value - threshold.min) / threshold.stdDev,\n Math.abs(value - threshold.max) / threshold.stdDev\n );\n\n if (distance > 3) return 'critical';\n if (distance > 2) return 'high';\n if (distance > 1) return 'medium';\n return 'low';\n }\n\n private calculateConfidence(value: number, threshold: { min: number; max: number; stdDev: number }): number {\n const distance = Math.min(\n Math.abs(value - threshold.min) / threshold.stdDev,\n Math.abs(value - threshold.max) / threshold.stdDev\n );\n\n return Math.min(0.99, 0.5 + (distance * 0.15));\n }\n\n private generateRecommendations(metric: MetricData): string[] {\n const recommendations: string[] = [];\n\n switch (metric.type) {\n case MetricType.MESSAGE_FAILED:\n recommendations.push(\n 'Check provider API status and connectivity',\n 'Verify template approval status',\n 'Validate recipient phone numbers',\n 'Review message content for compliance'\n );\n break;\n case MetricType.DELIVERY_RATE:\n recommendations.push(\n 'Monitor provider performance metrics',\n 'Check for network connectivity issues',\n 'Verify recipient opt-in status'\n );\n break;\n case MetricType.ERROR_RATE:\n recommendations.push(\n 'Investigate error patterns and root causes',\n 'Implement retry mechanisms for transient failures',\n 'Consider switching to backup provider'\n );\n break;\n default:\n recommendations.push(\n 'Monitor the metric closely',\n 'Investigate potential causes',\n 'Check system health and performance'\n );\n }\n\n return recommendations;\n }\n\n private calculateTrend(values: number[]): number {\n if (values.length < 2) return 0;\n\n // 간단한 선형 회귀\n const n = values.length;\n const x = Array.from({ length: n }, (_, i) => i);\n const sumX = x.reduce((a, b) => a + b, 0);\n const sumY = values.reduce((a, b) => a + b, 0);\n const sumXY = x.reduce((sum, xi, i) => sum + xi * values[i], 0);\n const sumXX = x.reduce((sum, xi) => sum + xi * xi, 0);\n\n const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);\n return slope;\n }\n}","import type {\n AnalyticsConfig,\n MetricData,\n AnalyticsQuery,\n AnalyticsResult,\n AggregatedMetric,\n InsightData,\n MetricType\n} from '../types/analytics.types';\nimport { MetricsCollector } from './metrics.collector';\nimport { ReportGenerator } from './report.generator';\nimport { InsightEngine } from './insight.engine';\n\nexport class AnalyticsService {\n private config: AnalyticsConfig;\n private metricsCollector: MetricsCollector;\n private reportGenerator: ReportGenerator;\n private insightEngine: InsightEngine;\n private metrics: Map<string, MetricData[]> = new Map();\n private aggregatedMetrics: Map<string, AggregatedMetric[]> = new Map();\n\n constructor(config: AnalyticsConfig) {\n this.config = config;\n this.metricsCollector = new MetricsCollector(config);\n this.reportGenerator = new ReportGenerator(config);\n this.insightEngine = new InsightEngine(config);\n\n // 주기적 집계 작업 시작\n this.startAggregationTasks();\n }\n\n /**\n * 메트릭 데이터 수집\n */\n async collectMetric(metric: MetricData): Promise<void> {\n if (!this.config.enabledMetrics.includes(metric.type)) {\n return;\n }\n\n await this.metricsCollector.collect(metric);\n\n // 실시간 추적이 활성화된 경우 즉시 처리\n if (this.config.enableRealTimeTracking) {\n await this.processRealTimeMetric(metric);\n }\n }\n\n /**\n * 분석 쿼리 실행\n */\n async query(query: AnalyticsQuery): Promise<AnalyticsResult> {\n const startTime = Date.now();\n\n // 쿼리 검증\n this.validateQuery(query);\n\n // 데이터 조회\n const data = await this.executeQuery(query);\n\n // 인사이트 생성 (옵션)\n const insights = await this.generateInsights(query, data);\n\n const executionTime = Date.now() - startTime;\n\n return {\n query,\n data,\n summary: {\n totalRecords: data.length,\n dateRange: query.dateRange,\n executionTime,\n },\n insights,\n };\n }\n\n /**\n * 실시간 메트릭 스트림\n */\n async *streamMetrics(types: MetricType[]): AsyncGenerator<MetricData> {\n // 실시간 메트릭 스트림 구현\n while (true) {\n const metrics = await this.metricsCollector.getRecentMetrics(types, 1000); // 최근 1초\n for (const metric of metrics) {\n yield metric;\n }\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n }\n\n /**\n * 대시보드 데이터 조회\n */\n async getDashboardData(timeRange: { start: Date; end: Date }) {\n const now = new Date();\n const previousPeriod = {\n start: new Date(timeRange.start.getTime() - (timeRange.end.getTime() - timeRange.start.getTime())),\n end: timeRange.start,\n };\n\n // 현재 기간 데이터\n const currentData = await this.query({\n metrics: this.config.enabledMetrics,\n dateRange: timeRange,\n interval: this.getOptimalInterval(timeRange),\n groupBy: ['provider', 'channel'],\n });\n\n // 이전 기간 데이터 (비교용)\n const previousData = await this.query({\n metrics: this.config.enabledMetrics,\n dateRange: previousPeriod,\n interval: this.getOptimalInterval(timeRange),\n groupBy: ['provider', 'channel'],\n });\n\n // KPI 계산\n const kpis = this.calculateKPIs(currentData.data, previousData.data);\n\n return {\n timeRange,\n kpis,\n metrics: currentData.data,\n insights: currentData.insights,\n trends: this.calculateTrends(currentData.data, previousData.data),\n };\n }\n\n /**\n * 이상 탐지\n */\n async detectAnomalies(metricType: MetricType, timeRange: { start: Date; end: Date }): Promise<InsightData[]> {\n return this.insightEngine.detectAnomalies(metricType, timeRange);\n }\n\n private async processRealTimeMetric(metric: MetricData): Promise<void> {\n // 실시간 이상 탐지\n const anomalies = await this.insightEngine.detectRealTimeAnomalies(metric);\n \n if (anomalies.length > 0) {\n // 이상 상황 알림 (웹훅 등)\n await this.notifyAnomalies(anomalies);\n }\n }\n\n private validateQuery(query: AnalyticsQuery): void {\n if (query.dateRange.start >= query.dateRange.end) {\n throw new Error('Invalid date range: start must be before end');\n }\n\n const maxRangeMs = 90 * 24 * 60 * 60 * 1000; // 90일\n if (query.dateRange.end.getTime() - query.dateRange.start.getTime() > maxRangeMs) {\n throw new Error('Date range too large: maximum 90 days');\n }\n\n if (query.limit && query.limit > 10000) {\n throw new Error('Limit too large: maximum 10000 records');\n }\n }\n\n private async executeQuery(query: AnalyticsQuery): Promise<AggregatedMetric[]> {\n // 실제 구현에서는 데이터베이스 쿼리를 실행\n // 여기서는 메모리 기반 구현 예시\n \n const interval = query.interval || this.getOptimalInterval(query.dateRange);\n const cacheKey = this.generateCacheKey(query);\n \n // 캐시된 데이터 확인\n if (this.aggregatedMetrics.has(cacheKey)) {\n return this.aggregatedMetrics.get(cacheKey)!;\n }\n\n // 집계 실행\n const aggregated = await this.performAggregation(query, interval);\n \n // 캐시 저장 (TTL 설정 필요)\n this.aggregatedMetrics.set(cacheKey, aggregated);\n \n return aggregated;\n }\n\n private async performAggregation(query: AnalyticsQuery, interval: string): Promise<AggregatedMetric[]> {\n // 집계 로직 구현\n // 실제로는 시계열 데이터베이스나 OLAP 엔진 사용\n return [];\n }\n\n private async generateInsights(query: AnalyticsQuery, data: AggregatedMetric[]): Promise<InsightData[]> {\n return this.insightEngine.generateInsights(query, data);\n }\n\n private getOptimalInterval(dateRange: { start: Date; end: Date }): 'minute' | 'hour' | 'day' | 'week' | 'month' {\n const durationMs = dateRange.end.getTime() - dateRange.start.getTime();\n const durationDays = durationMs / (24 * 60 * 60 * 1000);\n\n if (durationDays <= 1) return 'minute';\n if (durationDays <= 7) return 'hour';\n if (durationDays <= 30) return 'day';\n if (durationDays <= 90) return 'week';\n return 'month';\n }\n\n private calculateKPIs(current: AggregatedMetric[], previous: AggregatedMetric[]) {\n // KPI 계산 로직\n return {\n totalMessages: this.sumMetrics(current, 'message_sent'),\n deliveryRate: this.calculateRate(current, 'message_delivered', 'message_sent'),\n errorRate: this.calculateRate(current, 'message_failed', 'message_sent'),\n clickRate: this.calculateRate(current, 'message_clicked', 'message_delivered'),\n };\n }\n\n private calculateTrends(current: AggregatedMetric[], previous: AggregatedMetric[]) {\n // 트렌드 계산 로직\n return {};\n }\n\n private sumMetrics(metrics: AggregatedMetric[], type: string): number {\n return metrics\n .filter(m => m.type.toString() === type)\n .reduce((sum, m) => sum + m.aggregations.sum, 0);\n }\n\n private calculateRate(metrics: AggregatedMetric[], numerator: string, denominator: string): number {\n const num = this.sumMetrics(metrics, numerator);\n const den = this.sumMetrics(metrics, denominator);\n return den > 0 ? (num / den) * 100 : 0;\n }\n\n private generateCacheKey(query: AnalyticsQuery): string {\n return JSON.stringify({\n metrics: query.metrics.sort(),\n dateRange: query.dateRange,\n interval: query.interval,\n filters: query.filters,\n groupBy: query.groupBy?.sort(),\n });\n }\n\n private startAggregationTasks(): void {\n // 주기적 집계 작업 스케줄링\n for (const interval of this.config.aggregationIntervals) {\n this.scheduleAggregation(interval);\n }\n }\n\n private scheduleAggregation(interval: string): void {\n // 집계 작업 스케줄링 로직\n const scheduleMs = this.getScheduleInterval(interval);\n \n setInterval(async () => {\n try {\n await this.runAggregation(interval);\n } catch (error) {\n console.error(`Aggregation failed for interval ${interval}:`, error);\n }\n }, scheduleMs);\n }\n\n private getScheduleInterval(interval: string): number {\n switch (interval) {\n case 'minute': return 60 * 1000;\n case 'hour': return 60 * 60 * 1000;\n case 'day': return 24 * 60 * 60 * 1000;\n case 'week': return 7 * 24 * 60 * 60 * 1000;\n case 'month': return 30 * 24 * 60 * 60 * 1000;\n default: return 60 * 60 * 1000;\n }\n }\n\n private async runAggregation(interval: string): Promise<void> {\n // 실제 집계 작업 실행\n console.log(`Running ${interval} aggregation...`);\n }\n\n private async notifyAnomalies(anomalies: InsightData[]): Promise<void> {\n // 이상 상황 알림 로직 (웹훅, 이메일 등)\n for (const anomaly of anomalies) {\n if (anomaly.severity === 'critical' || anomaly.severity === 'high') {\n console.warn('Anomaly detected:', anomaly);\n // 실제 알림 전송\n }\n }\n }\n}","/**\n * Time Series Data Aggregator\n * 시계열 데이터 집계 및 다운샘플링\n */\n\nimport type { MetricData, AggregatedMetric, MetricType } from '../types/analytics.types';\n\nexport interface TimeWindow {\n start: Date;\n end: Date;\n interval: 'minute' | 'hour' | 'day' | 'week' | 'month';\n}\n\nexport interface AggregationOptions {\n fillGaps: boolean;\n fillValue: number;\n timezone?: string;\n}\n\nexport class TimeSeriesAggregator {\n private timezone: string;\n\n constructor(timezone = 'UTC') {\n this.timezone = timezone;\n }\n\n /**\n * 시계열 데이터를 지정된 간격으로 집계\n */\n async aggregate(\n metrics: MetricData[],\n interval: 'minute' | 'hour' | 'day' | 'week' | 'month',\n options: AggregationOptions = { fillGaps: false, fillValue: 0 }\n ): Promise<AggregatedMetric[]> {\n if (metrics.length === 0) {\n return [];\n }\n\n // 메트릭 타입별로 그룹화\n const groupedByType = this.groupByType(metrics);\n const aggregated: AggregatedMetric[] = [];\n\n for (const [type, typeMetrics] of groupedByType.entries()) {\n // 차원별로 그룹화\n const groupedByDimensions = this.groupByDimensions(typeMetrics);\n\n for (const [dimensionKey, dimensionMetrics] of groupedByDimensions.entries()) {\n const typeAggregated = await this.aggregateByInterval(\n dimensionMetrics,\n type,\n interval,\n JSON.parse(dimensionKey),\n options\n );\n aggregated.push(...typeAggregated);\n }\n }\n\n return aggregated.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n }\n\n /**\n * 롤링 윈도우 집계\n */\n async aggregateRolling(\n metrics: MetricData[],\n windowSize: number, // 분 단위\n step: number = windowSize // 분 단위\n ): Promise<AggregatedMetric[]> {\n if (metrics.length === 0) {\n return [];\n }\n\n const sortedMetrics = [...metrics].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n const startTime = sortedMetrics[0].timestamp;\n const endTime = sortedMetrics[sortedMetrics.length - 1].timestamp;\n \n const aggregated: AggregatedMetric[] = [];\n const windowMs = windowSize * 60 * 1000;\n const stepMs = step * 60 * 1000;\n\n let currentTime = startTime.getTime();\n const endTimeMs = endTime.getTime();\n\n while (currentTime <= endTimeMs) {\n const windowStart = new Date(currentTime);\n const windowEnd = new Date(currentTime + windowMs);\n\n const windowMetrics = sortedMetrics.filter(m => \n m.timestamp >= windowStart && m.timestamp < windowEnd\n );\n\n if (windowMetrics.length > 0) {\n const windowAggregated = await this.aggregateWindow(windowMetrics, windowStart);\n aggregated.push(...windowAggregated);\n }\n\n currentTime += stepMs;\n }\n\n return aggregated;\n }\n\n /**\n * 계절성 분해 (간단한 이동평균 기반)\n */\n async decomposeSeasonality(\n metrics: AggregatedMetric[],\n seasonLength: number = 24 // 시간 단위 (일별 패턴의 경우)\n ): Promise<{\n trend: AggregatedMetric[];\n seasonal: AggregatedMetric[];\n residual: AggregatedMetric[];\n }> {\n if (metrics.length < seasonLength * 2) {\n return { trend: [], seasonal: [], residual: [] };\n }\n\n const sortedMetrics = [...metrics].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n \n // 트렌드 계산 (이동평균)\n const trend = this.calculateMovingAverage(sortedMetrics, seasonLength);\n \n // 계절성 계산\n const seasonal = this.calculateSeasonalComponent(sortedMetrics, trend, seasonLength);\n \n // 잔차 계산\n const residual = this.calculateResidual(sortedMetrics, trend, seasonal);\n\n return { trend, seasonal, residual };\n }\n\n /**\n * 다운샘플링 (고해상도 → 저해상도)\n */\n async downsample(\n metrics: AggregatedMetric[],\n targetInterval: 'minute' | 'hour' | 'day' | 'week' | 'month'\n ): Promise<AggregatedMetric[]> {\n const groupedByTime = this.groupByTimeInterval(metrics, targetInterval);\n const downsampled: AggregatedMetric[] = [];\n\n for (const [timeKey, timeMetrics] of groupedByTime.entries()) {\n const timestamp = new Date(timeKey);\n \n // 메트릭 타입과 차원별로 그룹화\n const grouped = new Map<string, Map<string, AggregatedMetric[]>>();\n \n for (const metric of timeMetrics) {\n const typeKey = metric.type.toString();\n const dimensionKey = JSON.stringify(metric.dimensions);\n \n if (!grouped.has(typeKey)) {\n grouped.set(typeKey, new Map());\n }\n if (!grouped.get(typeKey)!.has(dimensionKey)) {\n grouped.get(typeKey)!.set(dimensionKey, []);\n }\n \n grouped.get(typeKey)!.get(dimensionKey)!.push(metric);\n }\n\n // 각 그룹별로 집계\n for (const [typeKey, typeGroups] of grouped.entries()) {\n for (const [dimensionKey, dimensionMetrics] of typeGroups.entries()) {\n const aggregations = this.calculateAggregations(dimensionMetrics.map(m => m.aggregations.sum));\n \n downsampled.push({\n type: dimensionMetrics[0].type,\n interval: targetInterval,\n timestamp,\n dimensions: JSON.parse(dimensionKey),\n aggregations\n });\n }\n }\n }\n\n return downsampled.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n }\n\n private groupByType(metrics: MetricData[]): Map<MetricType, MetricData[]> {\n const grouped = new Map<MetricType, MetricData[]>();\n \n for (const metric of metrics) {\n if (!grouped.has(metric.type)) {\n grouped.set(metric.type, []);\n }\n grouped.get(metric.type)!.push(metric);\n }\n \n return grouped;\n }\n\n private groupByDimensions(metrics: MetricData[]): Map<string, MetricData[]> {\n const grouped = new Map<string, MetricData[]>();\n \n for (const metric of metrics) {\n const key = JSON.stringify(metric.dimensions);\n if (!grouped.has(key)) {\n grouped.set(key, []);\n }\n grouped.get(key)!.push(metric);\n }\n \n return grouped;\n }\n\n private async aggregateByInterval(\n metrics: MetricData[],\n type: MetricType,\n interval: 'minute' | 'hour' | 'day' | 'week' | 'month',\n dimensions: Record<string, string>,\n options: AggregationOptions\n ): Promise<AggregatedMetric[]> {\n const grouped = this.groupByTimeInterval(metrics, interval);\n const aggregated: AggregatedMetric[] = [];\n\n // 시간 간격 채우기 (옵션이 활성화된 경우)\n if (options.fillGaps && grouped.size > 0) {\n const timeKeys = Array.from(grouped.keys()).sort((a, b) => new Date(a).getTime() - new Date(b).getTime());\n const filledGroups = this.fillTimeGaps(timeKeys, interval, options.fillValue);\n \n for (const [timeKey, defaultValue] of filledGroups.entries()) {\n if (!grouped.has(timeKey)) {\n grouped.set(timeKey, [{\n id: `filled_${Date.now()}`,\n type,\n timestamp: new Date(timeKey),\n value: defaultValue,\n dimensions\n }]);\n }\n }\n }\n\n for (const [timeKey, timeMetrics] of grouped.entries()) {\n const timestamp = new Date(timeKey);\n const values = timeMetrics.map(m => m.value);\n const aggregations = this.calculateAggregations(values);\n\n aggregated.push({\n type,\n interval,\n timestamp,\n dimensions,\n aggregations\n });\n }\n\n return aggregated;\n }\n\n private groupByTimeInterval(\n metrics: (MetricData | AggregatedMetric)[],\n interval: 'minute' | 'hour' | 'day' | 'week' | 'month'\n ): Map<string, any[]> {\n const grouped = new Map<string, any[]>();\n\n for (const metric of metrics) {\n const timeKey = this.getTimeIntervalKey(metric.timestamp, interval);\n if (!grouped.has(timeKey)) {\n grouped.set(timeKey, []);\n }\n grouped.get(timeKey)!.push(metric);\n }\n\n return grouped;\n }\n\n private getTimeIntervalKey(timestamp: Date, interval: 'minute' | 'hour' | 'day' | 'week' | 'month'): string {\n const date = new Date(timestamp);\n\n switch (interval) {\n case 'minute':\n date.setSeconds(0, 0);\n break;\n case 'hour':\n date.setMinutes(0, 0, 0);\n break;\n case 'day':\n date.setHours(0, 0, 0, 0);\n break;\n case 'week':\n const dayOfWeek = date.getDay();\n const diff = date.getDate() - dayOfWeek;\n date.setDate(diff);\n date.setHours(0, 0, 0, 0);\n break;\n case 'month':\n date.setDate(1);\n date.setHours(0, 0, 0, 0);\n break;\n }\n\n return date.toISOString();\n }\n\n private fillTimeGaps(\n timeKeys: string[],\n interval: 'minute' | 'hour' | 'day' | 'week' | 'month',\n fillValue: number\n ): Map<string, number> {\n const filled = new Map<string, number>();\n \n if (timeKeys.length < 2) {\n return filled;\n }\n\n const start = new Date(timeKeys[0]);\n const end = new Date(timeKeys[timeKeys.length - 1]);\n \n let current = new Date(start);\n \n while (current <= end) {\n const key = current.toISOString();\n filled.set(key, fillValue);\n \n // 다음 간격으로 이동\n switch (interval) {\n case 'minute':\n current.setMinutes(current.getMinutes() + 1);\n break;\n case 'hour':\n current.setHours(current.getHours() + 1);\n break;\n case 'day':\n current.setDate(current.getDate() + 1);\n break;\n case 'week':\n current.setDate(current.getDate() + 7);\n break;\n case 'month':\n current.setMonth(current.getMonth() + 1);\n break;\n }\n }\n\n return filled;\n }\n\n private calculateAggregations(values: number[]) {\n if (values.length === 0) {\n return { count: 0, sum: 0, avg: 0, min: 0, max: 0 };\n }\n\n const sum = values.reduce((acc, val) => acc + val, 0);\n const count = values.length;\n const avg = sum / count;\n const min = Math.min(...values);\n const max = Math.max(...values);\n\n return { count, sum, avg, min, max };\n }\n\n private async aggregateWindow(metrics: MetricData[], windowStart: Date): Promise<AggregatedMetric[]> {\n const grouped = this.groupByType(metrics);\n const aggregated: AggregatedMetric[] = [];\n\n for (const [type, typeMetrics] of grouped.entries()) {\n const dimensionGroups = this.groupByDimensions(typeMetrics);\n \n for (const [dimensionKey, dimensionMetrics] of dimensionGroups.entries()) {\n const values = dimensionMetrics.map(m => m.value);\n const aggregations = this.calculateAggregations(values);\n \n aggregated.push({\n type,\n interval: 'minute', // 롤링 윈도우는 분 단위로 처리\n timestamp: windowStart,\n dimensions: JSON.parse(dimensionKey),\n aggregations\n });\n }\n }\n\n return aggregated;\n }\n\n private calculateMovingAverage(metrics: AggregatedMetric[], windowSize: number): AggregatedMetric[] {\n const trend: AggregatedMetric[] = [];\n \n for (let i = Math.floor(windowSize / 2); i < metrics.length - Math.floor(windowSize / 2); i++) {\n const window = metrics.slice(i - Math.floor(windowSize / 2), i + Math.floor(windowSize / 2) + 1);\n const avgValue = window.reduce((sum, m) => sum + m.aggregations.avg, 0) / window.length;\n \n trend.push({\n ...metrics[i],\n aggregations: {\n ...metrics[i].aggregations,\n avg: avgValue,\n sum: avgValue * metrics[i].aggregations.count\n }\n });\n }\n \n return trend;\n }\n\n private calculateSeasonalComponent(\n metrics: AggregatedMetric[],\n trend: AggregatedMetric[],\n seasonLength: number\n ): AggregatedMetric[] {\n const seasonal: AggregatedMetric[] = [];\n const seasonalPattern = new Array(seasonLength).fill(0);\n const seasonalCounts = new Array(seasonLength).fill(0);\n \n // 계절성 패턴 계산\n for (let i = 0; i < metrics.length; i++) {\n const seasonIndex = i % seasonLength;\n const trendValue = trend.find(t => t.timestamp.getTime() === metrics[i].timestamp.getTime());\n \n if (trendValue) {\n seasonalPattern[seasonIndex] += metrics[i].aggregations.avg - trendValue.aggregations.avg;\n seasonalCounts[seasonIndex]++;\n }\n }\n \n // 평균 계산\n for (let i = 0; i < seasonLength; i++) {\n if (seasonalCounts[i] > 0) {\n seasonalPattern[i] /= seasonalCounts[i];\n }\n }\n \n // 계절성 컴포넌트 생성\n for (let i = 0; i < metrics.length; i++) {\n const seasonIndex = i % seasonLength;\n seasonal.push({\n ...metrics[i],\n aggregations: {\n ...metrics[i].aggregations,\n avg: seasonalPattern[seasonIndex],\n sum: seasonalPattern[seasonIndex] * metrics[i].aggregations.count\n }\n });\n }\n \n return seasonal;\n }\n\n private calculateResidual(\n original: AggregatedMetric[],\n trend: AggregatedMetric[],\n seasonal: AggregatedMetric[]\n ): AggregatedMetric[] {\n const residual: AggregatedMetric[] = [];\n \n for (let i = 0; i < original.length; i++) {\n const trendValue = trend.find(t => t.timestamp.getTime() === original[i].timestamp.getTime());\n const seasonalValue = seasonal[i];\n \n let residualValue = original[i].aggregations.avg;\n if (trendValue) {\n residualValue -= trendValue.aggregations.avg;\n }\n if (seasonalValue) {\n residualValue -= seasonalValue.aggregations.avg;\n }\n \n residual.push({\n ...original[i],\n aggregations: {\n ...original[i].aggregations,\n avg: residualValue,\n sum: residualValue * original[i].aggregations.count\n }\n });\n }\n \n return residual;\n }\n}","/**\n * Metric Aggregator\n * 메트릭별 특화 집계 로직\n */\n\nimport type { MetricData, AggregatedMetric } from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport interface AggregationRule {\n metricType: MetricType;\n aggregationType: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'rate' | 'percentile';\n dimensions: string[];\n conditions?: Array<{\n field: string;\n operator: 'equals' | 'not_equals' | 'gt' | 'lt' | 'contains';\n value: any;\n }>;\n percentile?: number; // percentile aggregation용\n}\n\nexport interface AggregatorConfig {\n rules: AggregationRule[];\n batchSize: number;\n flushInterval: number; // ms\n}\n\nexport class MetricAggregator {\n private config: AggregatorConfig;\n private buffer: Map<string, MetricData[]> = new Map();\n private aggregationCache: Map<string, AggregatedMetric[]> = new Map();\n\n constructor(config: AggregatorConfig) {\n this.config = config;\n this.startPeriodicFlush();\n }\n\n /**\n * 메트릭 추가 및 실시간 집계\n */\n async addMetric(metric: MetricData): Promise<void> {\n const bufferKey = this.getBufferKey(metric);\n \n if (!this.buffer.has(bufferKey)) {\n this.buffer.set(bufferKey, []);\n }\n \n this.buffer.get(bufferKey)!.push(metric);\n\n // 버퍼가 가득 찬 경우 즉시 집계\n if (this.buffer.get(bufferKey)!.length >= this.config.batchSize) {\n await this.flushBuffer(bufferKey);\n }\n }\n\n /**\n * 배치 메트릭 처리\n */\n async addMetrics(metrics: MetricData[]): Promise<void> {\n for (const metric of metrics) {\n await this.addMetric(metric);\n }\n }\n\n /**\n * 규칙 기반 집계 실행\n */\n async aggregateByRules(metrics: MetricData[]): Promise<AggregatedMetric[]> {\n const results: AggregatedMetric[] = [];\n\n for (const rule of this.config.rules) {\n const applicableMetrics = this.filterMetricsByRule(metrics, rule);\n if (applicableMetrics.length === 0) continue;\n\n const aggregated = await this.applyAggregationRule(applicableMetrics, rule);\n results.push(...aggregated);\n }\n\n return results;\n }\n\n /**\n * 커스텀 집계 (동적 규칙)\n */\n async aggregateCustom(\n metrics: MetricData[],\n groupBy: string[],\n aggregationType: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'rate',\n filters?: Record<string, any>\n ): Promise<AggregatedMetric[]> {\n let filteredMetrics = metrics;\n\n // 필터 적용\n if (filters) {\n filteredMetrics = this.applyFilters(metrics, filters);\n }\n\n // 그룹화\n const grouped = this.groupMetrics(filteredMetrics, groupBy);\n const results: AggregatedMetric[] = [];\n\n for (const [groupKey, groupMetrics] of grouped.entries()) {\n const dimensions = JSON.parse(groupKey);\n const aggregated = await this.performAggregation(groupMetrics, aggregationType, dimensions);\n results.push(...aggregated);\n }\n\n return results;\n }\n\n /**\n * 비율 계산 (예: 전환율, 오류율)\n */\n async calculateRates(\n numeratorMetrics: MetricData[],\n denominatorMetrics: MetricData[],\n groupBy: string[] = []\n ): Promise<AggregatedMetric[]> {\n const numGrouped = this.groupMetrics(numeratorMetrics, groupBy);\n const denGrouped = this.groupMetrics(denominatorMetrics, groupBy);\n const rates: AggregatedMetric[] = [];\n\n for (const [groupKey, numMetrics] of numGrouped.entries()) {\n const denMetrics = denGrouped.get(groupKey) || [];\n const dimensions = JSON.parse(groupKey);\n\n const numSum = numMetrics.reduce((sum, m) => sum + m.value, 0);\n const denSum = denMetrics.reduce((sum, m) => sum + m.value, 0);\n const rate = denSum > 0 ? (numSum / denSum) * 100 : 0;\n\n // 최신 타임스탬프 사용\n const latestTimestamp = new Date(Math.max(\n ...numMetrics.map(m => m.timestamp.getTime()),\n ...denMetrics.map(m => m.timestamp.getTime())\n ));\n\n rates.push({\n type: numMetrics[0]?.type || MetricType.DELIVERY_RATE,\n interval: 'minute',\n timestamp: latestTimestamp,\n dimensions,\n aggregations: {\n count: numMetrics.length + denMetrics.length,\n sum: rate,\n avg: rate,\n min: rate,\n max: rate\n }\n });\n }\n\n return rates;\n }\n\n /**\n * 백분위수 계산\n */\n async calculatePercentiles(\n metrics: MetricData[],\n percentiles: number[],\n groupBy: string[] = []\n ): Promise<AggregatedMetric[]> {\n const grouped = this.groupMetrics(metrics, groupBy);\n const results: AggregatedMetric[] = [];\n\n for (const [groupKey, groupMetrics] of grouped.entries()) {\n const dimensions = JSON.parse(groupKey);\n const values = groupMetrics.map(m => m.value).sort((a, b) => a - b);\n \n for (const percentile of percentiles) {\n const value = this.calculatePercentile(values, percentile);\n \n results.push({\n type: groupMetrics[0].type,\n interval: 'minute',\n timestamp: new Date(),\n dimensions: { ...dimensions, percentile: percentile.toString() },\n aggregations: {\n count: values.length,\n sum: value,\n avg: value,\n min: value,\n max: value\n }\n });\n }\n }\n\n return results;\n }\n\n /**\n * 슬라이딩 윈도우 집계\n */\n async aggregateSlidingWindow(\n metrics: MetricData[],\n windowSizeMs: number,\n stepMs: number,\n aggregationType: 'sum' | 'avg' | 'min' | 'max' | 'count'\n ): Promise<AggregatedMetric[]> {\n const sortedMetrics = [...metrics].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n if (sortedMetrics.length === 0) return [];\n\n const results: AggregatedMetric[] = [];\n const startTime = sortedMetrics[0].timestamp.getTime();\n const endTime = sortedMetrics[sortedMetrics.length - 1].timestamp.getTime();\n\n let currentTime = startTime;\n while (currentTime <= endTime) {\n const windowStart = new Date(currentTime);\n const windowEnd = new Date(currentTime + windowSizeMs);\n\n const windowMetrics = sortedMetrics.filter(m => \n m.timestamp >= windowStart && m.timestamp < windowEnd\n );\n\n if (windowMetrics.length > 0) {\n const aggregated = await this.performAggregation(windowMetrics, aggregationType, {});\n results.push(...aggregated);\n }\n\n currentTime += stepMs;\n }\n\n return results;\n }\n\n /**\n * 메트릭 정규화\n */\n async normalizeMetrics(\n metrics: AggregatedMetric[],\n method: 'minmax' | 'zscore' | 'robust'\n ): Promise<AggregatedMetric[]> {\n const byType = new Map<MetricType, AggregatedMetric[]>();\n \n // 타입별 그룹화\n for (const metric of metrics) {\n if (!byType.has(metric.type)) {\n byType.set(metric.type, []);\n }\n byType.get(metric.type)!.push(metric);\n }\n\n const normalized: AggregatedMetric[] = [];\n\n for (const [type, typeMetrics] of byType.entries()) {\n const values = typeMetrics.map(m => m.aggregations.avg);\n let normalizedValues: number[];\n\n switch (method) {\n case 'minmax':\n normalizedValues = this.minMaxNormalize(values);\n break;\n case 'zscore':\n normalizedValues = this.zScoreNormalize(values);\n break;\n case 'robust':\n normalizedValues = this.robustNormalize(values);\n break;\n default:\n normalizedValues = values;\n }\n\n for (let i = 0; i < typeMetrics.length; i++) {\n normalized.push({\n ...typeMetrics[i],\n aggregations: {\n ...typeMetrics[i].aggregations,\n avg: normalizedValues[i],\n sum: normalizedValues[i] * typeMetrics[i].aggregations.count\n }\n });\n }\n }\n\n return normalized;\n }\n\n private getBufferKey(metric: MetricData): string {\n return `${metric.type}_${JSON.stringify(metric.dimensions)}`;\n }\n\n private async flushBuffer(bufferKey: string): Promise<void> {\n const metrics = this.buffer.get(bufferKey);\n if (!metrics || metrics.length === 0) return;\n\n // 집계 수행\n const aggregated = await this.aggregateByRules(metrics);\n \n // 캐시에 저장\n if (aggregated.length > 0) {\n const cacheKey = `${bufferKey}_${Date.now()}`;\n this.aggregationCache.set(cacheKey, aggregated);\n }\n\n // 버퍼 클리어\n this.buffer.set(bufferKey, []);\n }\n\n private filterMetricsByRule(metrics: MetricData[], rule: AggregationRule): MetricData[] {\n let filtered = metrics.filter(m => m.type === rule.metricType);\n\n if (rule.conditions) {\n filtered = filtered.filter(metric => {\n return rule.conditions!.every(condition => {\n const value = this.getFieldValue(metric, condition.field);\n return this.evaluateCondition(value, condition.operator, condition.value);\n });\n });\n }\n\n return filtered;\n }\n\n private async applyAggregationRule(\n metrics: MetricData[],\n rule: AggregationRule\n ): Promise<AggregatedMetric[]> {\n const grouped = this.groupMetrics(metrics, rule.dimensions);\n const results: AggregatedMetric[] = [];\n\n for (const [groupKey, groupMetrics] of grouped.entries()) {\n const dimensions = JSON.parse(groupKey);\n \n if (rule.aggregationType === 'percentile') {\n if (rule.percentile) {\n const values = groupMetrics.map(m => m.value).sort((a, b) => a - b);\n const percentileValue = this.calculatePercentile(values, rule.percentile);\n \n results.push({\n type: rule.metricType,\n interval: 'minute',\n timestamp: new Date(),\n dimensions,\n aggregations: {\n count: values.length,\n sum: percentileValue,\n avg: percentileValue,\n min: percentileValue,\n max: percentileValue\n }\n });\n }\n // Skip if percentile but no percentile value specified\n } else {\n const aggregated = await this.performAggregation(groupMetrics, rule.aggregationType, dimensions);\n results.push(...aggregated);\n }\n }\n\n return results;\n }\n\n private groupMetrics(metrics: MetricData[], groupBy: string[]): Map<string, MetricData[]> {\n const grouped = new Map<string, MetricData[]>();\n\n for (const metric of metrics) {\n const groupKey = this.createGroupKey(metric, groupBy);\n \n if (!grouped.has(groupKey)) {\n grouped.set(groupKey, []);\n }\n grouped.get(groupKey)!.push(metric);\n }\n\n return grouped;\n }\n\n private createGroupKey(metric: MetricData, groupBy: string[]): string {\n const groupDimensions: Record<string, string> = {};\n \n for (const field of groupBy) {\n groupDimensions[field] = this.getFieldValue(metric, field);\n }\n \n return JSON.stringify(groupDimensions);\n }\n\n private getFieldValue(metric: MetricData, field: string): string {\n if (field === 'type') return metric.type.toString();\n if (field === 'timestamp') return metric.timestamp.toISOString();\n return metric.dimensions[field] || '';\n }\n\n private evaluateCondition(value: any, operator: string, expected: any): boolean {\n switch (operator) {\n case 'equals': return value === expected;\n case 'not_equals': return value !== expected;\n case 'gt': return Number(value) > Number(expected);\n case 'lt': return Number(value) < Number(expected);\n case 'contains': return String(value).includes(String(expected));\n default: return false;\n }\n }\n\n private async performAggregation(\n metrics: MetricData[],\n aggregationType: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'rate',\n dimensions: Record<string, string>\n ): Promise<AggregatedMetric[]> {\n if (metrics.length === 0) return [];\n\n const values = metrics.map(m => m.value);\n let aggregatedValue: number;\n\n switch (aggregationType) {\n case 'sum':\n aggregatedValue = values.reduce((sum, val) => sum + val, 0);\n break;\n case 'avg':\n aggregatedValue = values.reduce((sum, val) => sum + val, 0) / values.length;\n break;\n case 'min':\n aggregatedValue = Math.min(...values);\n break;\n case 'max':\n aggregatedValue = Math.max(...values);\n break;\n case 'count':\n aggregatedValue = values.length;\n break;\n case 'rate':\n // 비율 계산은 별도 메서드에서 처리\n aggregatedValue = 0;\n break;\n default:\n aggregatedValue = 0;\n }\n\n return [{\n type: metrics[0].type,\n interval: 'minute',\n timestamp: new Date(),\n dimensions,\n aggregations: {\n count: values.length,\n sum: aggregationType === 'sum' ? aggregatedValue : values.reduce((sum, val) => sum + val, 0),\n avg: aggregationType === 'avg' ? aggregatedValue : aggregatedValue,\n min: Math.min(...values),\n max: Math.max(...values)\n }\n }];\n }\n\n private applyFilters(metrics: MetricData[], filters: Record<string, any>): MetricData[] {\n return metrics.filter(metric => {\n return Object.entries(filters).every(([key, value]) => {\n const metricValue = this.getFieldValue(metric, key);\n return Array.isArray(value) ? value.includes(metricValue) : metricValue === value;\n });\n });\n }\n\n private calculatePercentile(values: number[], percentile: number): number {\n if (values.length === 0) return 0;\n \n const index = (percentile / 100) * (values.length - 1);\n const lower = Math.floor(index);\n const upper = Math.ceil(index);\n \n if (lower === upper) {\n return values[lower];\n }\n \n const weight = index - lower;\n return values[lower] * (1 - weight) + values[upper] * weight;\n }\n\n private minMaxNormalize(values: number[]): number[] {\n const min = Math.min(...values);\n const max = Math.max(...values);\n const range = max - min;\n \n return range === 0 ? values.map(() => 0) : values.map(v => (v - min) / range);\n }\n\n private zScoreNormalize(values: number[]): number[] {\n const mean = values.reduce((sum, v) => sum + v, 0) / values.length;\n const stdDev = Math.sqrt(values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length);\n \n return stdDev === 0 ? values.map(() => 0) : values.map(v => (v - mean) / stdDev);\n }\n\n private robustNormalize(values: number[]): number[] {\n const sorted = [...values].sort((a, b) => a - b);\n const median = this.calculatePercentile(sorted, 50);\n const mad = this.calculateMAD(sorted, median);\n \n return mad === 0 ? values.map(() => 0) : values.map(v => (v - median) / mad);\n }\n\n private calculateMAD(values: number[], median: number): number {\n const absDeviations = values.map(v => Math.abs(v - median));\n return this.calculatePercentile(absDeviations.sort((a, b) => a - b), 50);\n }\n\n private startPeriodicFlush(): void {\n setInterval(async () => {\n for (const bufferKey of this.buffer.keys()) {\n await this.flushBuffer(bufferKey);\n }\n }, this.config.flushInterval);\n }\n}","/**\n * Event Collector\n * 실시간 이벤트 수집 및 메트릭 변환\n */\n\nimport { EventEmitter } from 'events';\nimport type { MetricData } from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport interface EventData {\n id: string;\n type: string;\n timestamp: Date;\n source: string;\n payload: Record<string, any>;\n context?: {\n userId?: string;\n sessionId?: string;\n requestId?: string;\n userAgent?: string;\n ipAddress?: string;\n };\n}\n\nexport interface EventCollectorConfig {\n bufferSize: number;\n flushInterval: number; // ms\n enableDeduplication: boolean;\n deduplicationWindow: number; // ms\n enableSampling: boolean;\n samplingRate: number; // 0-1\n}\n\nexport interface EventProcessor {\n canProcess(event: EventData): boolean;\n process(event: EventData): Promise<MetricData[]>;\n}\n\nexport class EventCollector extends EventEmitter {\n private config: EventCollectorConfig;\n private buffer: EventData[] = [];\n private processors: Map<string, EventProcessor> = new Map();\n private recentEvents: Map<string, number> = new Map(); // 중복 제거용\n private metrics: MetricData[] = [];\n\n private defaultConfig: EventCollectorConfig = {\n bufferSize: 1000,\n flushInterval: 5000,\n enableDeduplication: true,\n deduplicationWindow: 60000, // 1분\n enableSampling: false,\n samplingRate: 1.0\n };\n\n constructor(config: Partial<EventCollectorConfig> = {}) {\n super();\n this.config = { ...this.defaultConfig, ...config };\n this.initializeDefaultProcessors();\n this.startPeriodicFlush();\n this.startCleanup();\n }\n\n /**\n * 이벤트 수집\n */\n async collectEvent(event: EventData): Promise<void> {\n // 샘플링 확인\n if (this.config.enableSampling && Math.random() > this.config.samplingRate) {\n return;\n }\n\n // 중복 제거\n if (this.config.enableDeduplication && this.isDuplicate(event)) {\n return;\n }\n\n // 이벤트 검증\n this.validateEvent(event);\n\n // 버퍼에 추가\n this.buffer.push(event);\n\n // 즉시 처리가 필요한 경우\n if (this.isHighPriorityEvent(event)) {\n await this.processEvent(event);\n }\n\n // 버퍼가 가득 찬 경우 플러시\n if (this.buffer.length >= this.config.bufferSize) {\n await this.flush();\n }\n\n this.emit('event:collected', event);\n }\n\n /**\n * 배치 이벤트 수집\n */\n async collectEvents(events: EventData[]): Promise<void> {\n for (const event of events) {\n await this.collectEvent(event);\n }\n }\n\n /**\n * 커스텀 이벤트 프로세서 등록\n */\n registerProcessor(name: string, processor: EventProcessor): void {\n this.processors.set(name, processor);\n this.emit('processor:registered', { name, processor });\n }\n\n /**\n * 이벤트 프로세서 제거\n */\n unregisterProcessor(name: string): boolean {\n const removed = this.processors.delete(name);\n if (removed) {\n this.emit('processor:unregistered', { name });\n }\n return removed;\n }\n\n /**\n * 수집된 메트릭 조회\n */\n getCollectedMetrics(since?: Date): MetricData[] {\n if (!since) {\n return [...this.metrics];\n }\n\n return this.metrics.filter(m => m.timestamp >= since);\n }\n\n /**\n * 실시간 메트릭 스트림\n */\n async *streamMetrics(): AsyncGenerator<MetricData[]> {\n while (true) {\n await new Promise(resolve => setTimeout(resolve, this.config.flushInterval));\n \n if (this.buffer.length > 0) {\n await this.flush();\n }\n\n const recentMetrics = this.getCollectedMetrics(\n new Date(Date.now() - this.config.flushInterval)\n );\n\n if (recentMetrics.length > 0) {\n yield recentMetrics;\n }\n }\n }\n\n /**\n * 이벤트 통계\n */\n getEventStats(): {\n totalEvents: number;\n eventsByType: Record<string, number>;\n eventsBySource: Record<string, number>;\n bufferSize: number;\n metricsGenerated: number;\n } {\n const eventsByType: Record<string, number> = {};\n const eventsBySource: Record<string, number> = {};\n\n // 버퍼의 이벤트 통계\n for (const event of this.buffer) {\n eventsByType[event.type] = (eventsByType[event.type] || 0) + 1;\n eventsBySource[event.source] = (eventsBySource[event.source] || 0) + 1;\n }\n\n return {\n totalEvents: this.buffer.length,\n eventsByType,\n eventsBySource,\n bufferSize: this.buffer.length,\n metricsGenerated: this.metrics.length\n };\n }\n\n /**\n * 버퍼 강제 플러시\n */\n async flush(): Promise<void> {\n if (this.buffer.length === 0) {\n return;\n }\n\n const events = [...this.buffer];\n this.buffer = [];\n\n try {\n const allMetrics: MetricData[] = [];\n\n for (const event of events) {\n const metrics = await this.processEvent(event);\n allMetrics.push(...metrics);\n }\n\n this.metrics.push(...allMetrics);\n\n // 메트릭 제한 (메모리 관리)\n if (this.metrics.length > 100000) {\n this.metrics = this.metrics.slice(-50000); // 최근 50,000개만 유지\n }\n\n this.emit('events:flushed', { \n eventCount: events.length, \n metricCount: allMetrics.length \n });\n\n } catch (error) {\n // 실패한 이벤트를 다시 버퍼에 추가\n this.buffer.unshift(...events);\n this.emit('flush:error', error);\n throw error;\n }\n }\n\n private async processEvent(event: EventData): Promise<MetricData[]> {\n const metrics: MetricData[] = [];\n\n for (const [name, processor] of this.processors.entries()) {\n try {\n if (processor.canProcess(event)) {\n const processorMetrics = await processor.process(event);\n metrics.push(...processorMetrics);\n }\n } catch (error) {\n this.emit('processor:error', { processorName: name, event, error });\n }\n }\n\n return metrics;\n }\n\n private validateEvent(event: EventData): void {\n if (!event.id) {\n throw new Error('Event ID is required');\n }\n\n if (!event.type) {\n throw new Error('Event type is required');\n }\n\n if (!event.timestamp || !(event.timestamp instanceof Date)) {\n throw new Error('Valid event timestamp is required');\n }\n\n if (!event.source) {\n throw new Error('Event source is required');\n }\n\n if (!event.payload || typeof event.payload !== 'object') {\n throw new Error('Event payload must be an object');\n }\n }\n\n private isDuplicate(event: EventData): boolean {\n const now = Date.now();\n const eventKey = `${event.id}_${event.type}_${event.source}`;\n const lastSeen = this.recentEvents.get(eventKey);\n\n if (lastSeen && (now - lastSeen) < this.config.deduplicationWindow) {\n return true;\n }\n\n this.recentEvents.set(eventKey, now);\n return false;\n }\n\n private isHighPriorityEvent(event: EventData): boolean {\n // 높은 우선순위 이벤트 타입들\n const highPriorityTypes = [\n 'message.failed',\n 'system.error',\n 'security.alert',\n 'performance.degradation'\n ];\n\n return highPriorityTypes.includes(event.type);\n }\n\n private initializeDefaultProcessors(): void {\n // 메시지 전송 프로세서\n this.registerProcessor('message-sent', {\n canProcess: (event) => event.type === 'message.sent',\n process: async (event) => {\n return [{\n id: `metric_${event.id}`,\n type: MetricType.MESSAGE_SENT,\n timestamp: event.timestamp,\n value: 1,\n dimensions: {\n provider: event.payload.provider || 'unknown',\n channel: event.payload.channel || 'unknown',\n template: event.payload.templateId || 'none'\n },\n metadata: {\n messageId: event.payload.messageId,\n recipientCount: event.payload.recipientCount || 1\n }\n }];\n }\n });\n\n // 메시지 전달 프로세서\n this.registerProcessor('message-delivered', {\n canProcess: (event) => event.type === 'message.delivered',\n process: async (event) => {\n return [{\n id: `metric_${event.id}`,\n type: MetricType.MESSAGE_DELIVERED,\n timestamp: event.timestamp,\n value: 1,\n dimensions: {\n provider: event.payload.provider || 'unknown',\n channel: event.payload.channel || 'unknown'\n },\n metadata: {\n messageId: event.payload.messageId,\n deliveryTime: event.payload.deliveryTime\n }\n }];\n }\n });\n\n // 메시지 실패 프로세서\n this.registerProcessor('message-failed', {\n canProcess: (event) => event.type === 'message.failed',\n process: async (event) => {\n return [{\n id: `metric_${event.id}`,\n type: MetricType.MESSAGE_FAILED,\n timestamp: event.timestamp,\n value: 1,\n dimensions: {\n provider: event.payload.provider || 'unknown',\n channel: event.payload.channel || 'unknown',\n errorCode: event.payload.errorCode || 'unknown'\n },\n metadata: {\n messageId: event.payload.messageId,\n errorMessage: event.payload.errorMessage,\n errorType: event.payload.errorType\n }\n }];\n }\n });\n\n // 메시지 클릭 프로세서\n this.registerProcessor('message-clicked', {\n canProcess: (event) => event.type === 'message.clicked' || event.type === 'link.clicked',\n process: async (event) => {\n return [{\n id: `metric_${event.id}`,\n type: MetricType.MESSAGE_CLICKED,\n timestamp: event.timestamp,\n value: 1,\n dimensions: {\n provider: event.payload.provider || 'unknown',\n channel: event.payload.channel || 'unknown',\n linkType: event.payload.linkType || 'unknown'\n },\n metadata: {\n messageId: event.payload.messageId,\n linkUrl: event.payload.linkUrl,\n userId: event.context?.userId\n }\n }];\n }\n });\n\n // 템플릿 사용 프로세서\n this.registerProcessor('template-used', {\n canProcess: (event) => event.type === 'template.used',\n process: async (event) => {\n return [{\n id: `metric_${event.id}`,\n type: MetricType.TEMPLATE_USAGE,\n timestamp: event.timestamp,\n value: 1,\n dimensions: {\n templateId: event.payload.templateId || 'unknown',\n provider: event.payload.provider || 'unknown',\n channel: event.payload.channel || 'unknown'\n },\n metadata: {\n templateName: event.payload.templateName,\n version: event.payload.version\n }\n }];\n }\n });\n\n // 채널 사용 프로세서\n this.registerProcessor('channel-used', {\n canProcess: (event) => ['message.sent', 'message.delivered'].includes(event.type),\n process: async (event) => {\n return [{\n id: `metric_channel_${event.id}`,\n type: MetricType.CHANNEL_USAGE,\n timestamp: event.timestamp,\n value: 1,\n dimensions: {\n channel: event.payload.channel || 'unknown',\n provider: event.payload.provider || 'unknown'\n }\n }];\n }\n });\n }\n\n private startPeriodicFlush(): void {\n setInterval(async () => {\n try {\n await this.flush();\n } catch (error) {\n this.emit('flush:error', error);\n }\n }, this.config.flushInterval);\n }\n\n private startCleanup(): void {\n // 중복 제거 캐시 정리 (매 10분)\n setInterval(() => {\n const cutoff = Date.now() - this.config.deduplicationWindow;\n \n for (const [key, timestamp] of this.recentEvents.entries()) {\n if (timestamp < cutoff) {\n this.recentEvents.delete(key);\n }\n }\n }, 10 * 60 * 1000);\n\n // 메트릭 정리 (매 시간)\n setInterval(() => {\n if (this.metrics.length > 100000) {\n this.metrics = this.metrics.slice(-50000);\n this.emit('metrics:cleaned', { remainingCount: this.metrics.length });\n }\n }, 60 * 60 * 1000);\n }\n}","/**\n * Webhook Collector\n * 웹훅을 통한 외부 이벤트 수집\n */\n\nimport { EventEmitter } from 'events';\nimport type { EventData } from './event.collector';\nimport type { MetricData, MetricType } from '../types/analytics.types';\n\nexport interface WebhookData {\n id: string;\n source: string;\n timestamp: Date;\n headers: Record<string, string>;\n body: any;\n signature?: string;\n}\n\nexport interface WebhookCollectorConfig {\n enableSignatureValidation: boolean;\n signatureHeader: string;\n secretKey?: string;\n allowedSources: string[];\n maxPayloadSize: number; // bytes\n rateLimitPerMinute: number;\n}\n\nexport interface WebhookTransformer {\n canTransform(webhook: WebhookData): boolean;\n transform(webhook: WebhookData): Promise<EventData[]>;\n}\n\nexport class WebhookCollector extends EventEmitter {\n private config: WebhookCollectorConfig;\n private transformers: Map<string, WebhookTransformer> = new Map();\n private requestCounts: Map<string, { count: number; resetTime: number }> = new Map();\n private processedWebhooks: WebhookData[] = [];\n\n private defaultConfig: WebhookCollectorConfig = {\n enableSignatureValidation: true,\n signatureHeader: 'x-signature',\n allowedSources: [],\n maxPayloadSize: 1024 * 1024, // 1MB\n rateLimitPerMinute: 1000\n };\n\n constructor(config: Partial<WebhookCollectorConfig> = {}) {\n super();\n this.config = { ...this.defaultConfig, ...config };\n this.initializeDefaultTransformers();\n this.startCleanup();\n }\n\n /**\n * 웹훅 수신 처리\n */\n async receiveWebhook(webhook: WebhookData): Promise<EventData[]> {\n // 레이트 리미팅 확인\n if (!this.checkRateLimit(webhook.source)) {\n throw new Error(`Rate limit exceeded for source: ${webhook.source}`);\n }\n\n // 웹훅 검증\n await this.validateWebhook(webhook);\n\n // 페이로드 크기 확인\n const payloadSize = JSON.stringify(webhook.body).length;\n if (payloadSize > this.config.maxPayloadSize) {\n throw new Error(`Payload size ${payloadSize} exceeds maximum ${this.config.maxPayloadSize}`);\n }\n\n // 이벤트 변환\n const events = await this.transformWebhook(webhook);\n\n // 웹훅 저장 (감사 목적)\n this.processedWebhooks.push(webhook);\n \n // 최근 1000개만 유지\n if (this.processedWebhooks.length > 1000) {\n this.processedWebhooks = this.processedWebhooks.slice(-500);\n }\n\n this.emit('webhook:received', { webhook, eventCount: events.length });\n return events;\n }\n\n /**\n * 웹훅 변환기 등록\n */\n registerTransformer(name: string, transformer: WebhookTransformer): void {\n this.transformers.set(name, transformer);\n this.emit('transformer:registered', { name });\n }\n\n /**\n * 웹훅 변환기 제거\n */\n unregisterTransformer(name: string): boolean {\n const removed = this.transformers.delete(name);\n if (removed) {\n this.emit('transformer:unregistered', { name });\n }\n return removed;\n }\n\n /**\n * 처리된 웹훅 조회\n */\n getProcessedWebhooks(since?: Date): WebhookData[] {\n if (!since) {\n return [...this.processedWebhooks];\n }\n\n return this.processedWebhooks.filter(w => w.timestamp >= since);\n }\n\n /**\n * 웹훅 통계\n */\n getWebhookStats(): {\n totalProcessed: number;\n bySource: Record<string, number>;\n recentCount: number;\n transformerCount: number;\n } {\n const bySource: Record<string, number> = {};\n const recentTime = new Date(Date.now() - 60 * 60 * 1000); // 1시간\n\n let recentCount = 0;\n for (const webhook of this.processedWebhooks) {\n bySource[webhook.source] = (bySource[webhook.source] || 0) + 1;\n \n if (webhook.timestamp >= recentTime) {\n recentCount++;\n }\n }\n\n return {\n totalProcessed: this.processedWebhooks.length,\n bySource,\n recentCount,\n transformerCount: this.transformers.size\n };\n }\n\n private async validateWebhook(webhook: WebhookData): Promise<void> {\n // 소스 검증\n if (this.config.allowedSources.length > 0 && !this.config.allowedSources.includes(webhook.source)) {\n throw new Error(`Source ${webhook.source} is not allowed`);\n }\n\n // 서명 검증\n if (this.config.enableSignatureValidation && this.config.secretKey) {\n await this.validateSignature(webhook);\n }\n\n // 기본 필드 검증\n if (!webhook.id) {\n throw new Error('Webhook ID is required');\n }\n\n if (!webhook.source) {\n throw new Error('Webhook source is required');\n }\n\n if (!webhook.timestamp || !(webhook.timestamp instanceof Date)) {\n throw new Error('Valid webhook timestamp is required');\n }\n }\n\n private async validateSignature(webhook: WebhookData): Promise<void> {\n const signature = webhook.headers[this.config.signatureHeader] || webhook.signature;\n \n if (!signature) {\n throw new Error(`Missing signature in header: ${this.config.signatureHeader}`);\n }\n\n // 간단한 HMAC 검증 (실제로는 crypto 모듈 사용)\n const expectedSignature = await this.generateSignature(webhook.body, this.config.secretKey!);\n \n if (signature !== expectedSignature) {\n throw new Error('Invalid webhook signature');\n }\n }\n\n private async generateSignature(payload: any, secret: string): Promise<string> {\n // 실제 구현에서는 crypto.createHmac 사용\n const payloadStr = JSON.stringify(payload);\n return `sha256=${payloadStr.length}_${secret.length}`; // 임시 구현\n }\n\n private checkRateLimit(source: string): boolean {\n const now = Date.now();\n const minuteKey = Math.floor(now / (60 * 1000));\n const rateLimitKey = `${source}_${minuteKey}`;\n \n const current = this.requestCounts.get(rateLimitKey) || { count: 0, resetTime: now + 60000 };\n \n if (current.count >= this.config.rateLimitPerMinute) {\n return false;\n }\n\n current.count++;\n this.requestCounts.set(rateLimitKey, current);\n return true;\n }\n\n private async transformWebhook(webhook: WebhookData): Promise<EventData[]> {\n const events: EventData[] = [];\n\n for (const [name, transformer] of this.transformers.entries()) {\n try {\n if (transformer.canTransform(webhook)) {\n const transformerEvents = await transformer.transform(webhook);\n events.push(...transformerEvents);\n }\n } catch (error) {\n this.emit('transformer:error', { transformerName: name, webhook, error });\n }\n }\n\n if (events.length === 0) {\n // 기본 변환 로직\n events.push(await this.defaultTransform(webhook));\n }\n\n return events;\n }\n\n private async defaultTransform(webhook: WebhookData): Promise<EventData> {\n return {\n id: `webhook_${webhook.id}`,\n type: `webhook.${webhook.source}`,\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: webhook.body,\n context: {\n requestId: webhook.id,\n userAgent: webhook.headers['user-agent'],\n ipAddress: webhook.headers['x-forwarded-for'] || webhook.headers['x-real-ip']\n }\n };\n }\n\n private initializeDefaultTransformers(): void {\n // SMS 프로바이더 웹훅 변환기\n this.registerTransformer('sms-provider', {\n canTransform: (webhook) => webhook.source.includes('sms') || webhook.source.includes('alimtalk'),\n transform: async (webhook) => {\n const events: EventData[] = [];\n const body = webhook.body;\n\n // 전송 완료 이벤트\n if (body.status === 'sent' || body.status === 'delivered') {\n events.push({\n id: `sms_delivered_${webhook.id}`,\n type: 'message.delivered',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n messageId: body.messageId || body.id,\n provider: webhook.source,\n channel: body.channel || 'sms',\n recipientNumber: body.to || body.recipient,\n deliveryTime: body.deliveredAt ? new Date(body.deliveredAt) : webhook.timestamp\n }\n });\n }\n\n // 전송 실패 이벤트\n if (body.status === 'failed' || body.status === 'error') {\n events.push({\n id: `sms_failed_${webhook.id}`,\n type: 'message.failed',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n messageId: body.messageId || body.id,\n provider: webhook.source,\n channel: body.channel || 'sms',\n errorCode: body.errorCode || 'unknown',\n errorMessage: body.errorMessage || body.error,\n errorType: body.errorType || 'delivery'\n }\n });\n }\n\n return events;\n }\n });\n\n // 알림톡 프로바이더 웹훅 변환기\n this.registerTransformer('alimtalk-provider', {\n canTransform: (webhook) => webhook.source.includes('alimtalk') || webhook.source.includes('kakao'),\n transform: async (webhook) => {\n const events: EventData[] = [];\n const body = webhook.body;\n\n // 버튼 클릭 이벤트\n if (body.eventType === 'click' || body.type === 'button_click') {\n events.push({\n id: `alimtalk_click_${webhook.id}`,\n type: 'message.clicked',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n messageId: body.messageId,\n provider: webhook.source,\n channel: 'alimtalk',\n linkType: body.buttonType || 'button',\n linkUrl: body.buttonUrl || body.url,\n buttonText: body.buttonText\n }\n });\n }\n\n // 메시지 상태 변경\n if (body.messageStatus || body.status) {\n const status = body.messageStatus || body.status;\n \n if (['DELIVERED', 'READ'].includes(status)) {\n events.push({\n id: `alimtalk_delivered_${webhook.id}`,\n type: 'message.delivered',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n messageId: body.messageId,\n provider: webhook.source,\n channel: 'alimtalk',\n deliveryTime: body.deliveredAt ? new Date(body.deliveredAt) : webhook.timestamp\n }\n });\n } else if (['FAILED', 'REJECTED'].includes(status)) {\n events.push({\n id: `alimtalk_failed_${webhook.id}`,\n type: 'message.failed',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n messageId: body.messageId,\n provider: webhook.source,\n channel: 'alimtalk',\n errorCode: body.failureCode || 'unknown',\n errorMessage: body.failureReason || 'Unknown error'\n }\n });\n }\n }\n\n return events;\n }\n });\n\n // 일반적인 메시징 프로바이더 웹훅 변환기\n this.registerTransformer('generic-messaging', {\n canTransform: (webhook) => {\n const body = webhook.body;\n return body && (body.messageId || body.id) && body.status;\n },\n transform: async (webhook) => {\n const events: EventData[] = [];\n const body = webhook.body;\n\n const basePayload = {\n messageId: body.messageId || body.id,\n provider: webhook.source,\n channel: body.channel || 'unknown'\n };\n\n switch (body.status) {\n case 'delivered':\n case 'read':\n events.push({\n id: `generic_delivered_${webhook.id}`,\n type: 'message.delivered',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n ...basePayload,\n deliveryTime: body.deliveredAt ? new Date(body.deliveredAt) : webhook.timestamp\n }\n });\n break;\n\n case 'failed':\n case 'rejected':\n case 'undelivered':\n events.push({\n id: `generic_failed_${webhook.id}`,\n type: 'message.failed',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n ...basePayload,\n errorCode: body.errorCode || body.error_code || 'unknown',\n errorMessage: body.errorMessage || body.error_message || 'Unknown error',\n errorType: body.errorType || 'delivery'\n }\n });\n break;\n\n case 'clicked':\n events.push({\n id: `generic_clicked_${webhook.id}`,\n type: 'message.clicked',\n timestamp: webhook.timestamp,\n source: webhook.source,\n payload: {\n ...basePayload,\n linkUrl: body.clickedUrl || body.url,\n linkType: body.linkType || 'link'\n }\n });\n break;\n }\n\n return events;\n }\n });\n }\n\n private startCleanup(): void {\n // 레이트 리미트 카운터 정리 (매 5분)\n setInterval(() => {\n const now = Date.now();\n \n for (const [key, data] of this.requestCounts.entries()) {\n if (now > data.resetTime) {\n this.requestCounts.delete(key);\n }\n }\n }, 5 * 60 * 1000);\n }\n}","/**\n * Anomaly Detector\n * 이상 징후 탐지 및 분석\n */\n\nimport type { MetricData, AggregatedMetric, InsightData } from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport interface AnomalyDetectionConfig {\n algorithms: AnomalyAlgorithm[];\n sensitivity: 'low' | 'medium' | 'high';\n minDataPoints: number;\n confidenceThreshold: number; // 0-1\n enableSeasonalAdjustment: boolean;\n}\n\nexport interface AnomalyAlgorithm {\n name: string;\n type: 'statistical' | 'ml' | 'rule-based';\n enabled: boolean;\n config: Record<string, any>;\n}\n\nexport interface Anomaly {\n id: string;\n metricType: MetricType;\n timestamp: Date;\n value: number;\n expectedValue: number;\n deviation: number;\n severity: 'low' | 'medium' | 'high' | 'critical';\n confidence: number;\n algorithm: string;\n dimensions: Record<string, string>;\n context?: {\n trend: 'increasing' | 'decreasing' | 'stable';\n seasonality: boolean;\n historicalComparison: number;\n };\n}\n\nexport class AnomalyDetector {\n private config: AnomalyDetectionConfig;\n private historicalData: Map<string, number[]> = new Map();\n private seasonalPatterns: Map<string, number[]> = new Map();\n private baselines: Map<string, { mean: number; stdDev: number; min: number; max: number }> = new Map();\n\n private defaultConfig: AnomalyDetectionConfig = {\n algorithms: [\n { name: 'zscore', type: 'statistical', enabled: true, config: { threshold: 3 } },\n { name: 'iqr', type: 'statistical', enabled: true, config: { multiplier: 1.5 } },\n { name: 'isolation', type: 'ml', enabled: false, config: { contamination: 0.1 } }\n ],\n sensitivity: 'medium',\n minDataPoints: 10,\n confidenceThreshold: 0.7,\n enableSeasonalAdjustment: true\n };\n\n constructor(config: Partial<AnomalyDetectionConfig> = {}) {\n this.config = { ...this.defaultConfig, ...config };\n }\n\n /**\n * 실시간 이상 탐지\n */\n async detectRealTimeAnomalies(metric: MetricData): Promise<Anomaly[]> {\n const key = this.getMetricKey(metric.type, metric.dimensions);\n const anomalies: Anomaly[] = [];\n\n // 히스토리 업데이트\n this.updateHistoricalData(key, metric.value);\n\n // 각 알고리즘으로 탐지\n for (const algorithm of this.config.algorithms) {\n if (!algorithm.enabled) continue;\n\n try {\n const anomaly = await this.runAlgorithm(metric, algorithm, key);\n if (anomaly && anomaly.confidence >= this.config.confidenceThreshold) {\n anomalies.push(anomaly);\n }\n } catch (error) {\n console.error(`Anomaly detection failed for algorithm ${algorithm.name}:`, error);\n }\n }\n\n return this.deduplicateAndRankAnomalies(anomalies);\n }\n\n /**\n * 배치 이상 탐지\n */\n async detectBatchAnomalies(\n metrics: AggregatedMetric[],\n timeWindow?: { start: Date; end: Date }\n ): Promise<Anomaly[]> {\n const anomalies: Anomaly[] = [];\n\n // 메트릭 타입별로 그룹화\n const groupedMetrics = this.groupMetricsByTypeAndDimensions(metrics);\n\n for (const [key, typeMetrics] of groupedMetrics.entries()) {\n const [metricType, dimensionsStr] = key.split('|');\n const dimensions = JSON.parse(dimensionsStr);\n\n // 시계열 데이터 준비\n const timeSeries = typeMetrics\n .sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())\n .map(m => ({ timestamp: m.timestamp, value: m.aggregations.avg }));\n\n if (timeSeries.length < this.config.minDataPoints) {\n continue;\n }\n\n // 계절성 조정\n let adjustedSeries = timeSeries;\n if (this.config.enableSeasonalAdjustment) {\n adjustedSeries = await this.adjustForSeasonality(timeSeries, key);\n }\n\n // 각 포인트에 대해 이상 탐지\n for (let i = Math.floor(adjustedSeries.length * 0.3); i < adjustedSeries.length; i++) {\n const point = adjustedSeries[i];\n const historicalWindow = adjustedSeries.slice(Math.max(0, i - 20), i);\n\n if (historicalWindow.length < this.config.minDataPoints) continue;\n\n const mockMetric: MetricData = {\n id: `batch_${i}`,\n type: metricType as MetricType,\n timestamp: point.timestamp,\n value: point.value,\n dimensions\n };\n\n const pointAnomalies = await this.detectRealTimeAnomalies(mockMetric);\n anomalies.push(...pointAnomalies);\n }\n }\n\n return this.deduplicateAndRankAnomalies(anomalies);\n }\n\n /**\n * 트렌드 변화 탐지\n */\n async detectTrendChanges(\n metrics: AggregatedMetric[],\n windowSize: number = 10\n ): Promise<InsightData[]> {\n const insights: InsightData[] = [];\n const groupedMetrics = this.groupMetricsByTypeAndDimensions(metrics);\n\n for (const [key, typeMetrics] of groupedMetrics.entries()) {\n const [metricType, dimensionsStr] = key.split('|');\n const dimensions = JSON.parse(dimensionsStr);\n\n const sortedMetrics = typeMetrics.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n \n if (sortedMetrics.length < windowSize * 2) continue;\n\n // 슬라이딩 윈도우로 트렌드 분석\n for (let i = windowSize; i < sortedMetrics.length - windowSize; i++) {\n const beforeWindow = sortedMetrics.slice(i - windowSize, i);\n const afterWindow = sortedMetrics.slice(i, i + windowSize);\n\n const beforeTrend = this.calculateTrend(beforeWindow.map(m => m.aggregations.avg));\n const afterTrend = this.calculateTrend(afterWindow.map(m => m.aggregations.avg));\n\n // 트렌드 변화 탐지\n const trendChange = Math.abs(afterTrend - beforeTrend);\n const significanceThreshold = this.getSensitivityThreshold('trend');\n\n if (trendChange > significanceThreshold) {\n insights.push({\n id: `trend_change_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n type: 'trend',\n title: `Trend Change Detected in ${metricType}`,\n description: `Significant trend change from ${beforeTrend.toFixed(2)} to ${afterTrend.toFixed(2)}`,\n severity: this.calculateTrendSeverity(trendChange, significanceThreshold),\n metric: metricType as MetricType,\n dimensions,\n value: afterTrend,\n expectedValue: beforeTrend,\n confidence: Math.min(0.95, 0.5 + (trendChange / significanceThreshold) * 0.3),\n actionable: trendChange > significanceThreshold * 2,\n recommendations: this.generateTrendRecommendations(beforeTrend, afterTrend, metricType as MetricType),\n detectedAt: sortedMetrics[i].timestamp\n });\n }\n }\n }\n\n return insights;\n }\n\n /**\n * 베이스라인 업데이트\n */\n async updateBaselines(metrics: AggregatedMetric[]): Promise<void> {\n const groupedMetrics = this.groupMetricsByTypeAndDimensions(metrics);\n\n for (const [key, typeMetrics] of groupedMetrics.entries()) {\n const values = typeMetrics.map(m => m.aggregations.avg);\n \n if (values.length < this.config.minDataPoints) continue;\n\n const mean = values.reduce((sum, v) => sum + v, 0) / values.length;\n const variance = values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length;\n const stdDev = Math.sqrt(variance);\n const min = Math.min(...values);\n const max = Math.max(...values);\n\n this.baselines.set(key, { mean, stdDev, min, max });\n }\n }\n\n private async runAlgorithm(\n metric: MetricData,\n algorithm: AnomalyAlgorithm,\n key: string\n ): Promise<Anomaly | null> {\n const historicalValues = this.historicalData.get(key) || [];\n \n if (historicalValues.length < this.config.minDataPoints) {\n return null;\n }\n\n switch (algorithm.name) {\n case 'zscore':\n return this.zScoreDetection(metric, historicalValues, algorithm.config, key);\n \n case 'iqr':\n return this.iqrDetection(metric, historicalValues, algorithm.config, key);\n \n case 'isolation':\n return this.isolationForestDetection(metric, historicalValues, algorithm.config, key);\n \n case 'threshold':\n return this.thresholdDetection(metric, algorithm.config, key);\n \n default:\n return null;\n }\n }\n\n private zScoreDetection(\n metric: MetricData,\n historicalValues: number[],\n config: any,\n key: string\n ): Anomaly | null {\n const mean = historicalValues.reduce((sum, v) => sum + v, 0) / historicalValues.length;\n const variance = historicalValues.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / historicalValues.length;\n const stdDev = Math.sqrt(variance);\n \n if (stdDev === 0) return null;\n\n const zScore = Math.abs((metric.value - mean) / stdDev);\n const threshold = config.threshold || 3;\n\n if (zScore > threshold) {\n const severity = this.calculateSeverity(zScore, threshold);\n const confidence = Math.min(0.99, zScore / threshold * 0.8);\n\n return {\n id: `zscore_${metric.id}`,\n metricType: metric.type,\n timestamp: metric.timestamp,\n value: metric.value,\n expectedValue: mean,\n deviation: zScore,\n severity,\n confidence,\n algorithm: 'zscore',\n dimensions: metric.dimensions,\n context: {\n trend: this.detectTrend(historicalValues),\n seasonality: false,\n historicalComparison: ((metric.value - mean) / mean) * 100\n }\n };\n }\n\n return null;\n }\n\n private iqrDetection(\n metric: MetricData,\n historicalValues: number[],\n config: any,\n key: string\n ): Anomaly | null {\n const sortedValues = [...historicalValues].sort((a, b) => a - b);\n const q1Index = Math.floor(sortedValues.length * 0.25);\n const q3Index = Math.floor(sortedValues.length * 0.75);\n \n const q1 = sortedValues[q1Index];\n const q3 = sortedValues[q3Index];\n const iqr = q3 - q1;\n \n const multiplier = config.multiplier || 1.5;\n const lowerBound = q1 - multiplier * iqr;\n const upperBound = q3 + multiplier * iqr;\n\n if (metric.value < lowerBound || metric.value > upperBound) {\n const expectedValue = (q1 + q3) / 2;\n const deviation = Math.abs(metric.value - expectedValue) / iqr;\n const severity = this.calculateSeverity(deviation, multiplier);\n const confidence = Math.min(0.99, deviation / multiplier * 0.7);\n\n return {\n id: `iqr_${metric.id}`,\n metricType: metric.type,\n timestamp: metric.timestamp,\n value: metric.value,\n expectedValue,\n deviation,\n severity,\n confidence,\n algorithm: 'iqr',\n dimensions: metric.dimensions,\n context: {\n trend: this.detectTrend(historicalValues),\n seasonality: false,\n historicalComparison: ((metric.value - expectedValue) / expectedValue) * 100\n }\n };\n }\n\n return null;\n }\n\n private isolationForestDetection(\n metric: MetricData,\n historicalValues: number[],\n config: any,\n key: string\n ): Anomaly | null {\n // 간단한 Isolation Forest 구현 (실제로는 더 복잡한 ML 라이브러리 사용)\n const contamination = config.contamination || 0.1;\n const threshold = this.calculateIsolationThreshold(historicalValues, contamination);\n \n const anomalyScore = this.calculateIsolationScore(metric.value, historicalValues);\n \n if (anomalyScore > threshold) {\n const expectedValue = historicalValues.reduce((sum, v) => sum + v, 0) / historicalValues.length;\n const severity = this.calculateSeverity(anomalyScore, threshold);\n const confidence = Math.min(0.99, anomalyScore / threshold * 0.9);\n\n return {\n id: `isolation_${metric.id}`,\n metricType: metric.type,\n timestamp: metric.timestamp,\n value: metric.value,\n expectedValue,\n deviation: anomalyScore,\n severity,\n confidence,\n algorithm: 'isolation',\n dimensions: metric.dimensions\n };\n }\n\n return null;\n }\n\n private thresholdDetection(\n metric: MetricData,\n config: any,\n key: string\n ): Anomaly | null {\n const upperThreshold = config.upperThreshold;\n const lowerThreshold = config.lowerThreshold;\n\n if (upperThreshold !== undefined && metric.value > upperThreshold) {\n return {\n id: `threshold_upper_${metric.id}`,\n metricType: metric.type,\n timestamp: metric.timestamp,\n value: metric.value,\n expectedValue: upperThreshold,\n deviation: (metric.value - upperThreshold) / upperThreshold,\n severity: 'high',\n confidence: 1.0,\n algorithm: 'threshold',\n dimensions: metric.dimensions\n };\n }\n\n if (lowerThreshold !== undefined && metric.value < lowerThreshold) {\n return {\n id: `threshold_lower_${metric.id}`,\n metricType: metric.type,\n timestamp: metric.timestamp,\n value: metric.value,\n expectedValue: lowerThreshold,\n deviation: (lowerThreshold - metric.value) / lowerThreshold,\n severity: 'high',\n confidence: 1.0,\n algorithm: 'threshold',\n dimensions: metric.dimensions\n };\n }\n\n return null;\n }\n\n private calculateSeverity(deviation: number, threshold: number): 'low' | 'medium' | 'high' | 'critical' {\n const ratio = deviation / threshold;\n \n if (ratio > 3) return 'critical';\n if (ratio > 2) return 'high';\n if (ratio > 1.5) return 'medium';\n return 'low';\n }\n\n private calculateTrendSeverity(change: number, threshold: number): 'low' | 'medium' | 'high' | 'critical' {\n const ratio = change / threshold;\n \n if (ratio > 4) return 'critical';\n if (ratio > 3) return 'high';\n if (ratio > 2) return 'medium';\n return 'low';\n }\n\n private getMetricKey(type: MetricType, dimensions: Record<string, string>): string {\n return `${type}|${JSON.stringify(dimensions)}`;\n }\n\n private updateHistoricalData(key: string, value: number): void {\n const history = this.historicalData.get(key) || [];\n history.push(value);\n\n // 최근 100개만 유지\n if (history.length > 100) {\n history.splice(0, history.length - 100);\n }\n\n this.historicalData.set(key, history);\n }\n\n private groupMetricsByTypeAndDimensions(metrics: AggregatedMetric[]): Map<string, AggregatedMetric[]> {\n const grouped = new Map<string, AggregatedMetric[]>();\n\n for (const metric of metrics) {\n const key = `${metric.type}|${JSON.stringify(metric.dimensions)}`;\n if (!grouped.has(key)) {\n grouped.set(key, []);\n }\n grouped.get(key)!.push(metric);\n }\n\n return grouped;\n }\n\n private async adjustForSeasonality(\n timeSeries: { timestamp: Date; value: number }[],\n key: string\n ): Promise<{ timestamp: Date; value: number }[]> {\n // 간단한 계절성 조정 (이동평균 기반)\n const windowSize = Math.min(24, Math.floor(timeSeries.length / 4)); // 24시간 또는 1/4 길이\n \n if (windowSize < 3) return timeSeries;\n\n return timeSeries.map((point, index) => {\n const windowStart = Math.max(0, index - Math.floor(windowSize / 2));\n const windowEnd = Math.min(timeSeries.length, windowStart + windowSize);\n const window = timeSeries.slice(windowStart, windowEnd);\n \n const seasonalAvg = window.reduce((sum, p) => sum + p.value, 0) / window.length;\n const adjustedValue = point.value - seasonalAvg + timeSeries.reduce((sum, p) => sum + p.value, 0) / timeSeries.length;\n \n return {\n timestamp: point.timestamp,\n value: adjustedValue\n };\n });\n }\n\n private calculateTrend(values: number[]): number {\n if (values.length < 2) return 0;\n\n const n = values.length;\n const x = Array.from({ length: n }, (_, i) => i);\n const sumX = x.reduce((a, b) => a + b, 0);\n const sumY = values.reduce((a, b) => a + b, 0);\n const sumXY = x.reduce((sum, xi, i) => sum + xi * values[i], 0);\n const sumXX = x.reduce((sum, xi) => sum + xi * xi, 0);\n\n return (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);\n }\n\n private detectTrend(values: number[]): 'increasing' | 'decreasing' | 'stable' {\n const trend = this.calculateTrend(values);\n const threshold = 0.1; // 임계값\n\n if (Math.abs(trend) < threshold) return 'stable';\n return trend > 0 ? 'increasing' : 'decreasing';\n }\n\n private getSensitivityThreshold(type: 'anomaly' | 'trend'): number {\n const baseThresholds = {\n anomaly: { low: 0.5, medium: 0.3, high: 0.1 },\n trend: { low: 2.0, medium: 1.0, high: 0.5 }\n };\n\n return baseThresholds[type][this.config.sensitivity];\n }\n\n private calculateIsolationThreshold(values: number[], contamination: number): number {\n // 간단한 임계값 계산 (실제로는 더 복잡한 ML 알고리즘 사용)\n const sorted = [...values].sort((a, b) => a - b);\n const cutoffIndex = Math.floor((1 - contamination) * sorted.length);\n return 0.6; // 간소화된 임계값\n }\n\n private calculateIsolationScore(value: number, historicalValues: number[]): number {\n // 간단한 isolation score 계산\n const mean = historicalValues.reduce((sum, v) => sum + v, 0) / historicalValues.length;\n const distances = historicalValues.map(v => Math.abs(v - value));\n const avgDistance = distances.reduce((sum, d) => sum + d, 0) / distances.length;\n \n return avgDistance / (Math.abs(value - mean) + 1);\n }\n\n private deduplicateAndRankAnomalies(anomalies: Anomaly[]): Anomaly[] {\n // 중복 제거 및 신뢰도순 정렬\n const unique = new Map<string, Anomaly>();\n \n for (const anomaly of anomalies) {\n const key = `${anomaly.metricType}_${JSON.stringify(anomaly.dimensions)}_${anomaly.timestamp.toISOString()}`;\n \n if (!unique.has(key) || unique.get(key)!.confidence < anomaly.confidence) {\n unique.set(key, anomaly);\n }\n }\n\n return Array.from(unique.values()).sort((a, b) => b.confidence - a.confidence);\n }\n\n private generateTrendRecommendations(\n beforeTrend: number,\n afterTrend: number,\n metricType: MetricType\n ): string[] {\n const recommendations: string[] = [];\n const isIncreasing = afterTrend > beforeTrend;\n const change = Math.abs(afterTrend - beforeTrend);\n\n switch (metricType) {\n case MetricType.MESSAGE_FAILED:\n if (isIncreasing) {\n recommendations.push(\n 'Investigate provider API issues',\n 'Review recent template changes',\n 'Check recipient data quality'\n );\n }\n break;\n \n case MetricType.DELIVERY_RATE:\n if (!isIncreasing) {\n recommendations.push(\n 'Monitor provider performance',\n 'Verify network connectivity',\n 'Check for carrier-specific issues'\n );\n }\n break;\n \n case MetricType.MESSAGE_SENT:\n if (isIncreasing) {\n recommendations.push(\n 'Monitor system capacity',\n 'Prepare for increased load',\n 'Check rate limiting settings'\n );\n } else {\n recommendations.push(\n 'Investigate traffic decrease',\n 'Check for system issues',\n 'Review campaign status'\n );\n }\n break;\n }\n\n if (change > 2.0) {\n recommendations.push('Consider immediate investigation due to significant change');\n }\n\n return recommendations;\n }\n}","/**\n * Recommendation Engine\n * 데이터 기반 추천 시스템\n */\n\nimport type { AggregatedMetric, InsightData } from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport interface RecommendationConfig {\n rules: RecommendationRule[];\n enableMachineLearning: boolean;\n confidenceThreshold: number;\n maxRecommendations: number;\n categories: RecommendationCategory[];\n}\n\nexport interface RecommendationRule {\n id: string;\n name: string;\n category: string;\n priority: number;\n conditions: RuleCondition[];\n actions: RecommendationAction[];\n enabled: boolean;\n}\n\nexport interface RuleCondition {\n metric: MetricType;\n operator: 'gt' | 'lt' | 'eq' | 'gte' | 'lte' | 'between' | 'trend';\n value: number | [number, number];\n timeWindow?: string; // '1h', '1d', '1w'\n dimensions?: Record<string, string>;\n}\n\nexport interface RecommendationAction {\n type: 'optimization' | 'cost-saving' | 'performance' | 'reliability' | 'security';\n title: string;\n description: string;\n impact: 'low' | 'medium' | 'high';\n effort: 'low' | 'medium' | 'high';\n steps: string[];\n estimatedBenefit?: {\n metric: MetricType;\n improvement: number;\n unit: string;\n };\n}\n\nexport interface Recommendation {\n id: string;\n category: string;\n priority: number;\n title: string;\n description: string;\n rationale: string;\n actions: RecommendationAction[];\n confidence: number;\n impact: 'low' | 'medium' | 'high';\n effort: 'low' | 'medium' | 'high';\n createdAt: Date;\n validUntil?: Date;\n metadata: {\n ruleIds: string[];\n triggeredBy: {\n metrics: { type: MetricType; value: number; timestamp: Date }[];\n conditions: string[];\n };\n estimatedROI?: number;\n };\n}\n\nexport interface RecommendationCategory {\n id: string;\n name: string;\n description: string;\n weight: number;\n}\n\nexport class RecommendationEngine {\n private config: RecommendationConfig;\n private recommendations: Map<string, Recommendation> = new Map();\n private ruleExecutionHistory: Map<string, Date[]> = new Map();\n\n private defaultConfig: RecommendationConfig = {\n rules: [],\n enableMachineLearning: false,\n confidenceThreshold: 0.7,\n maxRecommendations: 10,\n categories: [\n { id: 'cost', name: 'Cost Optimization', description: 'Reduce operational costs', weight: 0.8 },\n { id: 'performance', name: 'Performance', description: 'Improve system performance', weight: 0.9 },\n { id: 'reliability', name: 'Reliability', description: 'Enhance system reliability', weight: 1.0 },\n { id: 'security', name: 'Security', description: 'Strengthen security posture', weight: 0.95 }\n ]\n };\n\n constructor(config: Partial<RecommendationConfig> = {}) {\n this.config = { ...this.defaultConfig, ...config };\n this.initializeDefaultRules();\n }\n\n /**\n * 메트릭 기반 추천 생성\n */\n async generateRecommendations(metrics: AggregatedMetric[]): Promise<Recommendation[]> {\n const recommendations: Recommendation[] = [];\n\n // 규칙 기반 추천\n const ruleBasedRecommendations = await this.generateRuleBasedRecommendations(metrics);\n recommendations.push(...ruleBasedRecommendations);\n\n // 패턴 기반 추천\n const patternBasedRecommendations = await this.generatePatternBasedRecommendations(metrics);\n recommendations.push(...patternBasedRecommendations);\n\n // 비교 기반 추천\n const comparisonBasedRecommendations = await this.generateComparisonBasedRecommendations(metrics);\n recommendations.push(...comparisonBasedRecommendations);\n\n // ML 기반 추천 (활성화된 경우)\n if (this.config.enableMachineLearning) {\n const mlRecommendations = await this.generateMLBasedRecommendations(metrics);\n recommendations.push(...mlRecommendations);\n }\n\n // 중복 제거 및 우선순위 정렬\n const filteredRecommendations = this.deduplicateAndPrioritize(recommendations);\n\n // 저장\n for (const recommendation of filteredRecommendations) {\n this.recommendations.set(recommendation.id, recommendation);\n }\n\n return filteredRecommendations.slice(0, this.config.maxRecommendations);\n }\n\n /**\n * 특정 카테고리 추천 조회\n */\n getRecommendationsByCategory(category: string): Recommendation[] {\n return Array.from(this.recommendations.values())\n .filter(r => r.category === category)\n .sort((a, b) => b.priority - a.priority);\n }\n\n /**\n * 추천 실행 상태 업데이트\n */\n markRecommendationAsImplemented(recommendationId: string): boolean {\n const recommendation = this.recommendations.get(recommendationId);\n if (!recommendation) return false;\n\n // 구현된 추천을 제거하거나 별도 저장소로 이동\n this.recommendations.delete(recommendationId);\n return true;\n }\n\n /**\n * 추천 무시\n */\n dismissRecommendation(recommendationId: string, reason?: string): boolean {\n const recommendation = this.recommendations.get(recommendationId);\n if (!recommendation) return false;\n\n // 무시된 추천 처리\n this.recommendations.delete(recommendationId);\n return true;\n }\n\n /**\n * 추천 통계\n */\n getRecommendationStats(): {\n total: number;\n byCategory: Record<string, number>;\n byImpact: Record<string, number>;\n byPriority: Record<string, number>;\n } {\n const recommendations = Array.from(this.recommendations.values());\n const byCategory: Record<string, number> = {};\n const byImpact: Record<string, number> = {};\n const byPriority: Record<string, number> = {};\n\n for (const rec of recommendations) {\n byCategory[rec.category] = (byCategory[rec.category] || 0) + 1;\n byImpact[rec.impact] = (byImpact[rec.impact] || 0) + 1;\n \n const priorityLevel = rec.priority >= 8 ? 'high' : rec.priority >= 5 ? 'medium' : 'low';\n byPriority[priorityLevel] = (byPriority[priorityLevel] || 0) + 1;\n }\n\n return {\n total: recommendations.length,\n byCategory,\n byImpact,\n byPriority\n };\n }\n\n private async generateRuleBasedRecommendations(metrics: AggregatedMetric[]): Promise<Recommendation[]> {\n const recommendations: Recommendation[] = [];\n\n for (const rule of this.config.rules) {\n if (!rule.enabled) continue;\n\n try {\n const matchingMetrics = this.evaluateRuleConditions(rule, metrics);\n \n if (matchingMetrics.length > 0) {\n const recommendation = await this.createRecommendationFromRule(rule, matchingMetrics);\n if (recommendation && recommendation.confidence >= this.config.confidenceThreshold) {\n recommendations.push(recommendation);\n this.recordRuleExecution(rule.id);\n }\n }\n } catch (error) {\n console.error(`Rule execution failed for rule ${rule.id}:`, error);\n }\n }\n\n return recommendations;\n }\n\n private async generatePatternBasedRecommendations(metrics: AggregatedMetric[]): Promise<Recommendation[]> {\n const recommendations: Recommendation[] = [];\n\n // 시간 패턴 분석\n const timePatterns = this.analyzeTimePatterns(metrics);\n recommendations.push(...this.generateTimeBasedRecommendations(timePatterns));\n\n // 채널 사용 패턴 분석\n const channelPatterns = this.analyzeChannelPatterns(metrics);\n recommendations.push(...this.generateChannelBasedRecommendations(channelPatterns));\n\n // 오류 패턴 분석\n const errorPatterns = this.analyzeErrorPatterns(metrics);\n recommendations.push(...this.generateErrorBasedRecommendations(errorPatterns));\n\n return recommendations;\n }\n\n private async generateComparisonBasedRecommendations(metrics: AggregatedMetric[]): Promise<Recommendation[]> {\n const recommendations: Recommendation[] = [];\n\n // 프로바이더 성능 비교\n const providerComparison = this.compareProviderPerformance(metrics);\n recommendations.push(...this.generateProviderRecommendations(providerComparison));\n\n // 채널 효율성 비교\n const channelComparison = this.compareChannelEfficiency(metrics);\n recommendations.push(...this.generateChannelEfficiencyRecommendations(channelComparison));\n\n return recommendations;\n }\n\n private async generateMLBasedRecommendations(metrics: AggregatedMetric[]): Promise<Recommendation[]> {\n // ML 기반 추천 로직 (향후 구현)\n // 현재는 간단한 통계 기반 접근법 사용\n \n const recommendations: Recommendation[] = [];\n \n // 예측 모델을 통한 용량 계획\n const capacityRecommendations = await this.generateCapacityRecommendations(metrics);\n recommendations.push(...capacityRecommendations);\n\n return recommendations;\n }\n\n private evaluateRuleConditions(rule: RecommendationRule, metrics: AggregatedMetric[]): AggregatedMetric[] {\n const matchingMetrics: AggregatedMetric[] = [];\n\n for (const condition of rule.conditions) {\n const relevantMetrics = metrics.filter(m => {\n // 메트릭 타입 확인\n if (m.type !== condition.metric) return false;\n\n // 차원 필터 확인\n if (condition.dimensions) {\n for (const [key, value] of Object.entries(condition.dimensions)) {\n if (m.dimensions[key] !== value) return false;\n }\n }\n\n return true;\n });\n\n // 조건 평가\n for (const metric of relevantMetrics) {\n if (this.evaluateCondition(metric, condition)) {\n matchingMetrics.push(metric);\n }\n }\n }\n\n return matchingMetrics;\n }\n\n private evaluateCondition(metric: AggregatedMetric, condition: RuleCondition): boolean {\n const value = metric.aggregations.avg;\n\n switch (condition.operator) {\n case 'gt': return value > (condition.value as number);\n case 'lt': return value < (condition.value as number);\n case 'gte': return value >= (condition.value as number);\n case 'lte': return value <= (condition.value as number);\n case 'eq': return value === (condition.value as number);\n case 'between': \n const [min, max] = condition.value as [number, number];\n return value >= min && value <= max;\n case 'trend':\n // 트렌드 조건은 더 복잡한 로직 필요\n return false;\n default:\n return false;\n }\n }\n\n private async createRecommendationFromRule(\n rule: RecommendationRule,\n matchingMetrics: AggregatedMetric[]\n ): Promise<Recommendation | null> {\n if (rule.actions.length === 0) return null;\n\n const confidence = this.calculateRuleConfidence(rule, matchingMetrics);\n const impact = this.calculateAggregateImpact(rule.actions);\n const effort = this.calculateAggregateEffort(rule.actions);\n\n return {\n id: `rule_${rule.id}_${Date.now()}`,\n category: rule.category,\n priority: rule.priority,\n title: rule.name,\n description: `Based on analysis of ${matchingMetrics.length} metrics`,\n rationale: this.generateRationale(rule, matchingMetrics),\n actions: rule.actions,\n confidence,\n impact,\n effort,\n createdAt: new Date(),\n validUntil: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 1주일\n metadata: {\n ruleIds: [rule.id],\n triggeredBy: {\n metrics: matchingMetrics.map(m => ({\n type: m.type,\n value: m.aggregations.avg,\n timestamp: m.timestamp\n })),\n conditions: rule.conditions.map(c => `${c.metric} ${c.operator} ${c.value}`)\n }\n }\n };\n }\n\n private analyzeTimePatterns(metrics: AggregatedMetric[]): any {\n // 시간별 사용 패턴 분석\n const hourlyUsage = new Map<number, number>();\n const dailyUsage = new Map<number, number>();\n\n for (const metric of metrics) {\n const hour = metric.timestamp.getHours();\n const dayOfWeek = metric.timestamp.getDay();\n\n hourlyUsage.set(hour, (hourlyUsage.get(hour) || 0) + metric.aggregations.sum);\n dailyUsage.set(dayOfWeek, (dailyUsage.get(dayOfWeek) || 0) + metric.aggregations.sum);\n }\n\n return { hourlyUsage, dailyUsage };\n }\n\n private generateTimeBasedRecommendations(timePatterns: any): Recommendation[] {\n const recommendations: Recommendation[] = [];\n const { hourlyUsage, dailyUsage } = timePatterns;\n\n // 피크 시간 분석\n const peakHour = Array.from(hourlyUsage.entries() as Iterable<[number, number]>).reduce((max: [number, number], curr: [number, number]) => \n curr[1] > max[1] ? curr : max, [0, 0] as [number, number]\n );\n\n if (peakHour[1] > 0) {\n recommendations.push({\n id: `time_peak_${Date.now()}`,\n category: 'performance',\n priority: 7,\n title: 'Optimize for Peak Hours',\n description: `Peak usage occurs at ${peakHour[0]}:00. Consider load balancing optimizations.`,\n rationale: 'High traffic concentration during peak hours may impact performance',\n actions: [{\n type: 'performance',\n title: 'Implement Load Balancing',\n description: 'Configure load balancing to distribute traffic during peak hours',\n impact: 'medium',\n effort: 'medium',\n steps: [\n 'Set up multiple provider connections',\n 'Implement round-robin distribution',\n 'Monitor performance during peak hours'\n ]\n }],\n confidence: 0.8,\n impact: 'medium',\n effort: 'medium',\n createdAt: new Date(),\n metadata: {\n ruleIds: [],\n triggeredBy: {\n metrics: [],\n conditions: [`Peak hour usage: ${peakHour[1]} at ${peakHour[0]}:00`]\n }\n }\n });\n }\n\n return recommendations;\n }\n\n private analyzeChannelPatterns(metrics: AggregatedMetric[]): any {\n const channelUsage = new Map<string, { sent: number; delivered: number; failed: number }>();\n\n for (const metric of metrics) {\n const channel = metric.dimensions.channel || 'unknown';\n const stats = channelUsage.get(channel) || { sent: 0, delivered: 0, failed: 0 };\n\n if (metric.type === MetricType.MESSAGE_SENT) {\n stats.sent += metric.aggregations.sum;\n } else if (metric.type === MetricType.MESSAGE_DELIVERED) {\n stats.delivered += metric.aggregations.sum;\n } else if (metric.type === MetricType.MESSAGE_FAILED) {\n stats.failed += metric.aggregations.sum;\n }\n\n channelUsage.set(channel, stats);\n }\n\n return { channelUsage };\n }\n\n private generateChannelBasedRecommendations(channelPatterns: any): Recommendation[] {\n const recommendations: Recommendation[] = [];\n const { channelUsage } = channelPatterns;\n\n for (const [channel, stats] of channelUsage.entries()) {\n const deliveryRate = stats.sent > 0 ? (stats.delivered / stats.sent) * 100 : 0;\n const failureRate = stats.sent > 0 ? (stats.failed / stats.sent) * 100 : 0;\n\n if (deliveryRate < 90 && stats.sent > 100) {\n recommendations.push({\n id: `channel_delivery_${channel}_${Date.now()}`,\n category: 'reliability',\n priority: 8,\n title: `Improve ${channel} Channel Reliability`,\n description: `${channel} channel has ${deliveryRate.toFixed(1)}% delivery rate`,\n rationale: 'Low delivery rate impacts customer experience and wastes resources',\n actions: [{\n type: 'reliability',\n title: 'Investigate Channel Issues',\n description: `Analyze and fix delivery issues for ${channel} channel`,\n impact: 'high',\n effort: 'medium',\n steps: [\n `Review ${channel} provider configuration`,\n 'Check template approval status',\n 'Analyze failure patterns',\n 'Implement fallback mechanisms'\n ]\n }],\n confidence: 0.9,\n impact: 'high',\n effort: 'medium',\n createdAt: new Date(),\n metadata: {\n ruleIds: [],\n triggeredBy: {\n metrics: [],\n conditions: [`${channel} delivery rate: ${deliveryRate.toFixed(1)}%`]\n }\n }\n });\n }\n }\n\n return recommendations;\n }\n\n private analyzeErrorPatterns(metrics: AggregatedMetric[]): any {\n const errorsByCode = new Map<string, number>();\n const errorsByProvider = new Map<string, number>();\n\n for (const metric of metrics) {\n if (metric.type === MetricType.MESSAGE_FAILED) {\n const errorCode = metric.dimensions.errorCode || 'unknown';\n const provider = metric.dimensions.provider || 'unknown';\n\n errorsByCode.set(errorCode, (errorsByCode.get(errorCode) || 0) + metric.aggregations.sum);\n errorsByProvider.set(provider, (errorsByProvider.get(provider) || 0) + metric.aggregations.sum);\n }\n }\n\n return { errorsByCode, errorsByProvider };\n }\n\n private generateErrorBasedRecommendations(errorPatterns: any): Recommendation[] {\n const recommendations: Recommendation[] = [];\n const { errorsByCode, errorsByProvider } = errorPatterns;\n\n // 가장 빈번한 오류 코드 분석\n if (errorsByCode.size > 0) {\n const topError = Array.from(errorsByCode.entries() as Iterable<[string, number]>).reduce((max: [string, number], curr: [string, number]) => \n curr[1] > max[1] ? curr : max\n );\n\n if (topError[1] > 10) {\n recommendations.push({\n id: `error_${topError[0]}_${Date.now()}`,\n category: 'reliability',\n priority: 9,\n title: `Address Frequent Error: ${topError[0]}`,\n description: `Error code ${topError[0]} occurred ${topError[1]} times`,\n rationale: 'Frequent errors indicate systematic issues that need attention',\n actions: [{\n type: 'reliability',\n title: 'Fix Recurring Error',\n description: `Investigate and resolve error code ${topError[0]}`,\n impact: 'high',\n effort: 'high',\n steps: [\n 'Analyze error logs and patterns',\n 'Identify root cause',\n 'Implement fix or workaround',\n 'Add monitoring for this error type'\n ]\n }],\n confidence: 0.95,\n impact: 'high',\n effort: 'high',\n createdAt: new Date(),\n metadata: {\n ruleIds: [],\n triggeredBy: {\n metrics: [],\n conditions: [`Error ${topError[0]}: ${topError[1]} occurrences`]\n }\n }\n });\n }\n }\n\n return recommendations;\n }\n\n private compareProviderPerformance(metrics: AggregatedMetric[]): any {\n const providerStats = new Map<string, { sent: number; delivered: number; avgResponseTime: number }>();\n\n for (const metric of metrics) {\n const provider = metric.dimensions.provider;\n if (!provider) continue;\n\n const stats = providerStats.get(provider) || { sent: 0, delivered: 0, avgResponseTime: 0 };\n\n if (metric.type === MetricType.MESSAGE_SENT) {\n stats.sent += metric.aggregations.sum;\n } else if (metric.type === MetricType.MESSAGE_DELIVERED) {\n stats.delivered += metric.aggregations.sum;\n }\n\n providerStats.set(provider, stats);\n }\n\n return { providerStats };\n }\n\n private generateProviderRecommendations(providerComparison: any): Recommendation[] {\n const recommendations: Recommendation[] = [];\n const { providerStats } = providerComparison;\n\n const providers = Array.from(providerStats.entries() as Iterable<[string, { sent: number; delivered: number; avgResponseTime: number }]>).map(([provider, stats]) => ({\n provider,\n deliveryRate: stats.sent > 0 ? (stats.delivered / stats.sent) * 100 : 0,\n volume: stats.sent\n }));\n\n if (providers.length > 1) {\n const bestProvider = providers.reduce((best: { provider: string; deliveryRate: number; volume: number }, curr: { provider: string; deliveryRate: number; volume: number }) => \n curr.deliveryRate > best.deliveryRate ? curr : best\n );\n\n const worstProvider = providers.reduce((worst: { provider: string; deliveryRate: number; volume: number }, curr: { provider: string; deliveryRate: number; volume: number }) => \n curr.deliveryRate < worst.deliveryRate ? curr : worst\n );\n\n if (bestProvider.deliveryRate - worstProvider.deliveryRate > 10) {\n recommendations.push({\n id: `provider_optimization_${Date.now()}`,\n category: 'cost',\n priority: 6,\n title: 'Optimize Provider Usage',\n description: `${bestProvider.provider} has ${bestProvider.deliveryRate.toFixed(1)}% delivery rate vs ${worstProvider.provider} at ${worstProvider.deliveryRate.toFixed(1)}%`,\n rationale: 'Shifting traffic to better-performing providers can improve delivery rates and reduce costs',\n actions: [{\n type: 'optimization',\n title: 'Rebalance Provider Traffic',\n description: 'Increase traffic to high-performing providers',\n impact: 'medium',\n effort: 'low',\n steps: [\n `Reduce traffic allocation to ${worstProvider.provider}`,\n `Increase traffic allocation to ${bestProvider.provider}`,\n 'Monitor performance changes',\n 'Adjust allocation based on results'\n ]\n }],\n confidence: 0.85,\n impact: 'medium',\n effort: 'low',\n createdAt: new Date(),\n metadata: {\n ruleIds: [],\n triggeredBy: {\n metrics: [],\n conditions: [\n `${bestProvider.provider}: ${bestProvider.deliveryRate.toFixed(1)}%`,\n `${worstProvider.provider}: ${worstProvider.deliveryRate.toFixed(1)}%`\n ]\n }\n }\n });\n }\n }\n\n return recommendations;\n }\n\n private compareChannelEfficiency(metrics: AggregatedMetric[]): any {\n // 채널별 효율성 비교 로직\n return {};\n }\n\n private generateChannelEfficiencyRecommendations(channelComparison: any): Recommendation[] {\n return [];\n }\n\n private async generateCapacityRecommendations(metrics: AggregatedMetric[]): Promise<Recommendation[]> {\n const recommendations: Recommendation[] = [];\n\n // 간단한 용량 예측 (실제로는 더 정교한 ML 모델 사용)\n const messageSentMetrics = metrics.filter(m => m.type === MetricType.MESSAGE_SENT);\n \n if (messageSentMetrics.length > 0) {\n const recentVolume = messageSentMetrics\n .filter(m => m.timestamp > new Date(Date.now() - 24 * 60 * 60 * 1000))\n .reduce((sum, m) => sum + m.aggregations.sum, 0);\n\n const historicalAverage = messageSentMetrics\n .reduce((sum, m) => sum + m.aggregations.sum, 0) / messageSentMetrics.length;\n\n if (recentVolume > historicalAverage * 1.5) {\n recommendations.push({\n id: `capacity_scale_${Date.now()}`,\n category: 'performance',\n priority: 7,\n title: 'Consider Capacity Scaling',\n description: `Recent volume (${recentVolume}) is 50% above historical average (${historicalAverage.toFixed(0)})`,\n rationale: 'Sustained high volume may require additional capacity',\n actions: [{\n type: 'performance',\n title: 'Scale Infrastructure',\n description: 'Prepare for increased load by scaling infrastructure',\n impact: 'high',\n effort: 'high',\n steps: [\n 'Monitor system resource utilization',\n 'Prepare additional provider connections',\n 'Review rate limiting configurations',\n 'Plan for peak capacity scenarios'\n ]\n }],\n confidence: 0.75,\n impact: 'high',\n effort: 'high',\n createdAt: new Date(),\n metadata: {\n ruleIds: [],\n triggeredBy: {\n metrics: [],\n conditions: [`Recent volume: ${recentVolume}, Historical average: ${historicalAverage.toFixed(0)}`]\n }\n }\n });\n }\n }\n\n return recommendations;\n }\n\n private calculateRuleConfidence(rule: RecommendationRule, matchingMetrics: AggregatedMetric[]): number {\n // 조건 만족도와 데이터 품질을 기반으로 신뢰도 계산\n const baseConfidence = 0.5;\n const dataQualityBonus = Math.min(0.3, matchingMetrics.length / 10);\n const priorityBonus = rule.priority / 10 * 0.2;\n\n return Math.min(0.99, baseConfidence + dataQualityBonus + priorityBonus);\n }\n\n private calculateAggregateImpact(actions: RecommendationAction[]): 'low' | 'medium' | 'high' {\n const impactScores = { low: 1, medium: 2, high: 3 };\n const avgScore = actions.reduce((sum, action) => sum + impactScores[action.impact], 0) / actions.length;\n \n if (avgScore >= 2.5) return 'high';\n if (avgScore >= 1.5) return 'medium';\n return 'low';\n }\n\n private calculateAggregateEffort(actions: RecommendationAction[]): 'low' | 'medium' | 'high' {\n const effortScores = { low: 1, medium: 2, high: 3 };\n const avgScore = actions.reduce((sum, action) => sum + effortScores[action.effort], 0) / actions.length;\n \n if (avgScore >= 2.5) return 'high';\n if (avgScore >= 1.5) return 'medium';\n return 'low';\n }\n\n private generateRationale(rule: RecommendationRule, matchingMetrics: AggregatedMetric[]): string {\n const metricSummary = matchingMetrics.length > 0 \n ? `Based on ${matchingMetrics.length} metrics showing concerning patterns`\n : 'Based on rule evaluation';\n \n return `${metricSummary}. ${rule.name} conditions have been met, indicating potential optimization opportunities.`;\n }\n\n private recordRuleExecution(ruleId: string): void {\n const history = this.ruleExecutionHistory.get(ruleId) || [];\n history.push(new Date());\n \n // 최근 100개 실행 기록만 유지\n if (history.length > 100) {\n history.splice(0, history.length - 100);\n }\n \n this.ruleExecutionHistory.set(ruleId, history);\n }\n\n private deduplicateAndPrioritize(recommendations: Recommendation[]): Recommendation[] {\n // 유사한 추천 제거\n const uniqueRecommendations = new Map<string, Recommendation>();\n \n for (const rec of recommendations) {\n const key = `${rec.category}_${rec.title}`;\n \n if (!uniqueRecommendations.has(key) || \n uniqueRecommendations.get(key)!.confidence < rec.confidence) {\n uniqueRecommendations.set(key, rec);\n }\n }\n\n // 우선순위 및 신뢰도 기준 정렬\n return Array.from(uniqueRecommendations.values())\n .sort((a, b) => {\n if (a.priority !== b.priority) return b.priority - a.priority;\n return b.confidence - a.confidence;\n });\n }\n\n private initializeDefaultRules(): void {\n this.config.rules.push(\n {\n id: 'low-delivery-rate',\n name: 'Low Delivery Rate Alert',\n category: 'reliability',\n priority: 9,\n conditions: [\n { metric: MetricType.DELIVERY_RATE, operator: 'lt', value: 90 }\n ],\n actions: [{\n type: 'reliability',\n title: 'Improve Delivery Rate',\n description: 'Investigate and fix delivery rate issues',\n impact: 'high',\n effort: 'medium',\n steps: [\n 'Check provider API status',\n 'Review template approval status',\n 'Validate recipient phone numbers',\n 'Consider switching providers'\n ]\n }],\n enabled: true\n },\n {\n id: 'high-error-rate',\n name: 'High Error Rate Warning',\n category: 'reliability',\n priority: 8,\n conditions: [\n { metric: MetricType.ERROR_RATE, operator: 'gt', value: 10 }\n ],\n actions: [{\n type: 'reliability',\n title: 'Reduce Error Rate',\n description: 'Address high error rates',\n impact: 'high',\n effort: 'high',\n steps: [\n 'Analyze error patterns',\n 'Fix common error causes',\n 'Implement better error handling',\n 'Add monitoring and alerts'\n ]\n }],\n enabled: true\n },\n {\n id: 'cost-optimization-sms',\n name: 'SMS to AlimTalk Migration',\n category: 'cost',\n priority: 6,\n conditions: [\n { metric: MetricType.CHANNEL_USAGE, operator: 'gt', value: 1000, dimensions: { channel: 'sms' } }\n ],\n actions: [{\n type: 'cost-saving',\n title: 'Migrate to AlimTalk',\n description: 'Switch eligible SMS messages to AlimTalk for cost savings',\n impact: 'medium',\n effort: 'medium',\n steps: [\n 'Identify AlimTalk-eligible messages',\n 'Create AlimTalk templates',\n 'Implement fallback logic',\n 'Monitor cost savings'\n ],\n estimatedBenefit: {\n metric: MetricType.MESSAGE_SENT,\n improvement: 30,\n unit: 'percent cost reduction'\n }\n }],\n enabled: true\n }\n );\n }\n}","/**\n * Dashboard Report Generator\n * 실시간 대시보드 데이터 생성\n */\n\nimport type { \n AggregatedMetric, \n AnalyticsQuery, \n AnalyticsResult,\n InsightData \n} from '../types/analytics.types';\nimport { MetricType } from '../types/analytics.types';\n\nexport interface DashboardConfig {\n refreshInterval: number; // ms\n timeRange: {\n default: string; // '1h', '1d', '1w', '1m'\n options: string[];\n };\n widgets: DashboardWidget[];\n filters: DashboardFilter[];\n}\n\nexport interface DashboardWidget {\n id: string;\n type: 'metric' | 'chart' | 'table' | 'gauge' | 'heatmap' | 'trend';\n title: string;\n description?: string;\n position: { x: number; y: number; width: number; height: number };\n query: AnalyticsQuery;\n visualization: VisualizationConfig;\n refreshInterval?: number;\n}\n\nexport interface VisualizationConfig {\n chartType?: 'line' | 'bar' | 'pie' | 'area' | 'scatter';\n aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count';\n groupBy?: string[];\n colors?: string[];\n yAxis?: { min?: number; max?: number; label?: string };\n xAxis?: { label?: string };\n showLegend?: boolean;\n showGrid?: boolean;\n}\n\nexport interface DashboardFilter {\n id: string;\n name: string;\n type: 'select' | 'date' | 'range' | 'multi-select';\n field: string;\n options?: Array<{ value: string; label: string }>;\n defaultValue?: any;\n}\n\nexport interface DashboardData {\n timestamp: Date;\n timeRange: { start: Date; end: Date };\n kpis: KPIData[];\n widgets: WidgetData[];\n insights: InsightData[];\n filters: Record<string, any>;\n}\n\nexport interface KPIData {\n id: string;\n name: string;\n value: number;\n previousValue?: number;\n change?: number;\n changePercent?: number;\n trend: 'up' | 'down' | 'stable';\n unit?: string;\n target?: number;\n status: 'good' | 'warning' | 'critical';\n}\n\nexport interface WidgetData {\n id: string;\n title: string;\n type: string;\n data: any;\n lastUpdated: Date;\n error?: string;\n}\n\nexport class DashboardGenerator {\n private config: DashboardConfig;\n private dataCache: Map<string, { data: any; timestamp: Date }> = new Map();\n\n private defaultConfig: DashboardConfig = {\n refreshInterval: 30000, // 30초\n timeRange: {\n default: '1h',\n options: ['15m', '1h', '4h', '1d', '1w', '1m']\n },\n widgets: [],\n filters: [\n {\n id: 'provider',\n name: 'Provider',\n type: 'multi-select',\n field: 'provider',\n options: []\n },\n {\n id: 'channel',\n name: 'Channel',\n type: 'multi-select',\n field: 'channel',\n options: []\n }\n ]\n };\n\n constructor(config: Partial<DashboardConfig> = {}) {\n this.config = { ...this.defaultConfig, ...config };\n this.initializeDefaultWidgets();\n }\n\n /**\n * 대시보드 데이터 생성\n */\n async generateDashboard(\n timeRange: { start: Date; end: Date },\n filters: Record<string, any> = {},\n metrics: AggregatedMetric[] = []\n ): Promise<DashboardData> {\n const dashboard: DashboardData = {\n timestamp: new Date(),\n timeRange,\n kpis: [],\n widgets: [],\n insights: [],\n filters\n };\n\n try {\n // KPI 계산\n dashboard.kpis = await this.calculateKPIs(metrics, timeRange, filters);\n\n // 위젯 데이터 생성\n dashboard.widgets = await this.generateWidgetData(metrics, timeRange, filters);\n\n // 인사이트 생성 (외부에서 주입)\n dashboard.insights = [];\n\n } catch (error) {\n console.error('Dashboard generation failed:', error);\n }\n\n return dashboard;\n }\n\n /**\n * 실시간 대시보드 스트림\n */\n async *streamDashboard(\n timeRange: { start: Date; end: Date },\n filters: Record<string, any> = {}\n ): AsyncGenerator<DashboardData> {\n while (true) {\n const dashboard = await this.generateDashboard(timeRange, filters);\n yield dashboard;\n \n await new Promise(resolve => setTimeout(resolve, this.config.refreshInterval));\n }\n }\n\n /**\n * 특정 위젯 데이터 업데이트\n */\n async updateWidget(\n widgetId: string,\n metrics: AggregatedMetric[],\n timeRange: { start: Date; end: Date },\n filters: Record<string, any> = {}\n ): Promise<WidgetData | null> {\n const widget = this.config.widgets.find(w => w.id === widgetId);\n if (!widget) return null;\n\n try {\n const data = await this.generateWidgetData([widget], metrics, timeRange, filters);\n return data[0] || null;\n } catch (error) {\n return {\n id: widgetId,\n title: widget.title,\n type: widget.type,\n data: null,\n lastUpdated: new Date(),\n error: error instanceof Error ? error.message : 'Unknown error'\n };\n }\n }\n\n /**\n * 대시보드 구성 업데이트\n */\n updateConfig(config: Partial<DashboardConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * 위젯 추가\n */\n addWidget(widget: DashboardWidget): void {\n this.config.widgets.push(widget);\n }\n\n /**\n * 위젯 제거\n */\n removeWidget(widgetId: string): boolean {\n const index = this.config.widgets.findIndex(w => w.id === widgetId);\n if (index >= 0) {\n this.config.widgets.splice(index, 1);\n return true;\n }\n return false;\n }\n\n private async calculateKPIs(\n metrics: AggregatedMetric[],\n timeRange: { start: Date; end: Date },\n filters: Record<string, any>\n ): Promise<KPIData[]> {\n const kpis: KPIData[] = [];\n\n // 이전 기간 데이터 계산을 위한 시간 범위\n const duration = timeRange.end.getTime() - timeRange.start.getTime();\n const previousTimeRange = {\n start: new Date(timeRange.start.getTime() - duration),\n end: timeRange.start\n };\n\n // 메시지 전송량 KPI\n const sentMetrics = this.filterMetrics(metrics, MetricType.MESSAGE_SENT, filters);\n const totalSent = sentMetrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n const previousSent = this.calculatePreviousPeriodValue(sentMetrics, previousTimeRange);\n \n kpis.push({\n id: 'total_sent',\n name: 'Total Messages Sent',\n value: totalSent,\n previousValue: previousSent,\n change: totalSent - previousSent,\n changePercent: previousSent > 0 ? ((totalSent - previousSent) / previousSent) * 100 : 0,\n trend: totalSent > previousSent ? 'up' : totalSent < previousSent ? 'down' : 'stable',\n unit: 'messages',\n status: 'good'\n });\n\n // 전달률 KPI\n const deliveredMetrics = this.filterMetrics(metrics, MetricType.MESSAGE_DELIVERED, filters);\n const totalDelivered = deliveredMetrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n const deliveryRate = totalSent > 0 ? (totalDelivered / totalSent) * 100 : 0;\n \n kpis.push({\n id: 'delivery_rate',\n name: 'Delivery Rate',\n value: deliveryRate,\n trend: deliveryRate >= 95 ? 'up' : deliveryRate >= 90 ? 'stable' : 'down',\n unit: '%',\n target: 95,\n status: deliveryRate >= 95 ? 'good' : deliveryRate >= 85 ? 'warning' : 'critical'\n });\n\n // 오류율 KPI\n const failedMetrics = this.filterMetrics(metrics, MetricType.MESSAGE_FAILED, filters);\n const totalFailed = failedMetrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n const errorRate = totalSent > 0 ? (totalFailed / totalSent) * 100 : 0;\n \n kpis.push({\n id: 'error_rate',\n name: 'Error Rate',\n value: errorRate,\n trend: errorRate <= 5 ? 'down' : errorRate <= 10 ? 'stable' : 'up',\n unit: '%',\n target: 5,\n status: errorRate <= 5 ? 'good' : errorRate <= 15 ? 'warning' : 'critical'\n });\n\n // 클릭률 KPI (전달된 메시지 기준)\n const clickedMetrics = this.filterMetrics(metrics, MetricType.MESSAGE_CLICKED, filters);\n const totalClicked = clickedMetrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n const clickRate = totalDelivered > 0 ? (totalClicked / totalDelivered) * 100 : 0;\n \n kpis.push({\n id: 'click_rate',\n name: 'Click Rate',\n value: clickRate,\n trend: 'stable',\n unit: '%',\n status: 'good'\n });\n\n return kpis;\n }\n\n private async generateWidgetData(\n metrics: AggregatedMetric[],\n timeRange: { start: Date; end: Date },\n filters: Record<string, any>\n ): Promise<WidgetData[]>;\n private async generateWidgetData(\n widgets: DashboardWidget[],\n metrics: AggregatedMetric[],\n timeRange: { start: Date; end: Date },\n filters: Record<string, any>\n ): Promise<WidgetData[]>;\n private async generateWidgetData(...args: any[]): Promise<WidgetData[]> {\n let widgets: DashboardWidget[];\n let metrics: AggregatedMetric[];\n let timeRange: { start: Date; end: Date };\n let filters: Record<string, any>;\n\n if (args.length === 3) {\n // 첫 번째 오버로드\n [metrics, timeRange, filters] = args;\n widgets = this.config.widgets;\n } else {\n // 두 번째 오버로드\n [widgets, metrics, timeRange, filters] = args;\n }\n\n const widgetData: WidgetData[] = [];\n\n for (const widget of widgets) {\n try {\n const data = await this.generateSingleWidgetData(widget, metrics, timeRange, filters);\n widgetData.push({\n id: widget.id,\n title: widget.title,\n type: widget.type,\n data,\n lastUpdated: new Date()\n });\n } catch (error) {\n widgetData.push({\n id: widget.id,\n title: widget.title,\n type: widget.type,\n data: null,\n lastUpdated: new Date(),\n error: error instanceof Error ? error.message : 'Unknown error'\n });\n }\n }\n\n return widgetData;\n }\n\n private async generateSingleWidgetData(\n widget: DashboardWidget,\n metrics: AggregatedMetric[],\n timeRange: { start: Date; end: Date },\n filters: Record<string, any>\n ): Promise<any> {\n // 캐시 확인\n const cacheKey = `${widget.id}_${JSON.stringify(filters)}_${timeRange.start.getTime()}_${timeRange.end.getTime()}`;\n const cached = this.dataCache.get(cacheKey);\n const cacheExpiration = widget.refreshInterval || this.config.refreshInterval;\n \n if (cached && (Date.now() - cached.timestamp.getTime()) < cacheExpiration) {\n return cached.data;\n }\n\n // 메트릭 필터링\n const filteredMetrics = this.applyQueryFilters(metrics, widget.query, filters);\n\n let data: any;\n\n switch (widget.type) {\n case 'metric':\n data = this.generateMetricWidgetData(filteredMetrics, widget.visualization);\n break;\n case 'chart':\n data = this.generateChartWidgetData(filteredMetrics, widget.visualization);\n break;\n case 'table':\n data = this.generateTableWidgetData(filteredMetrics, widget.visualization);\n break;\n case 'gauge':\n data = this.generateGaugeWidgetData(filteredMetrics, widget.visualization);\n break;\n case 'heatmap':\n data = this.generateHeatmapWidgetData(filteredMetrics, widget.visualization);\n break;\n case 'trend':\n data = this.generateTrendWidgetData(filteredMetrics, widget.visualization);\n break;\n default:\n data = null;\n }\n\n // 캐시 저장\n this.dataCache.set(cacheKey, { data, timestamp: new Date() });\n\n return data;\n }\n\n private generateMetricWidgetData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n const aggregation = config.aggregation || 'sum';\n let value = 0;\n\n switch (aggregation) {\n case 'sum':\n value = metrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n break;\n case 'avg':\n value = metrics.reduce((sum, m) => sum + m.aggregations.avg, 0) / Math.max(metrics.length, 1);\n break;\n case 'min':\n value = Math.min(...metrics.map(m => m.aggregations.min));\n break;\n case 'max':\n value = Math.max(...metrics.map(m => m.aggregations.max));\n break;\n case 'count':\n value = metrics.length;\n break;\n }\n\n return {\n value: isFinite(value) ? value : 0,\n formatted: this.formatValue(value, config),\n timestamp: new Date()\n };\n }\n\n private generateChartWidgetData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n const chartType = config.chartType || 'line';\n \n if (config.groupBy && config.groupBy.length > 0) {\n return this.generateGroupedChartData(metrics, config);\n }\n\n // 시계열 데이터\n const sortedMetrics = metrics.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n \n const data = sortedMetrics.map(metric => ({\n x: metric.timestamp,\n y: this.getAggregatedValue(metric, config.aggregation || 'avg'),\n label: metric.timestamp.toISOString()\n }));\n\n return {\n type: chartType,\n data: [{\n name: 'Series 1',\n data,\n color: config.colors?.[0] || '#3b82f6'\n }],\n options: {\n xAxis: config.xAxis,\n yAxis: config.yAxis,\n showLegend: config.showLegend ?? true,\n showGrid: config.showGrid ?? true\n }\n };\n }\n\n private generateGroupedChartData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n const groupBy = config.groupBy![0]; // 첫 번째 그룹화 필드 사용\n const grouped = new Map<string, AggregatedMetric[]>();\n\n for (const metric of metrics) {\n const key = metric.dimensions[groupBy] || 'Unknown';\n if (!grouped.has(key)) {\n grouped.set(key, []);\n }\n grouped.get(key)!.push(metric);\n }\n\n const series = Array.from(grouped.entries()).map(([name, groupMetrics], index) => ({\n name,\n data: groupMetrics\n .sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())\n .map(m => ({\n x: m.timestamp,\n y: this.getAggregatedValue(m, config.aggregation || 'avg')\n })),\n color: config.colors?.[index % (config.colors?.length || 1)] || this.getDefaultColor(index)\n }));\n\n return {\n type: config.chartType || 'line',\n data: series,\n options: {\n xAxis: config.xAxis,\n yAxis: config.yAxis,\n showLegend: config.showLegend ?? true,\n showGrid: config.showGrid ?? true\n }\n };\n }\n\n private generateTableWidgetData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n const groupBy = config.groupBy || ['provider', 'channel'];\n const grouped = new Map<string, AggregatedMetric[]>();\n\n for (const metric of metrics) {\n const key = groupBy.map(field => metric.dimensions[field] || 'Unknown').join('|');\n if (!grouped.has(key)) {\n grouped.set(key, []);\n }\n grouped.get(key)!.push(metric);\n }\n\n const columns = [\n ...groupBy.map(field => ({ key: field, title: field.charAt(0).toUpperCase() + field.slice(1) })),\n { key: 'count', title: 'Count' },\n { key: 'sum', title: 'Sum' },\n { key: 'avg', title: 'Average' }\n ];\n\n const rows = Array.from(grouped.entries()).map(([key, groupMetrics]) => {\n const dimensions = key.split('|');\n const row: any = {};\n \n groupBy.forEach((field, index) => {\n row[field] = dimensions[index];\n });\n\n row.count = groupMetrics.length;\n row.sum = groupMetrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n row.avg = groupMetrics.reduce((sum, m) => sum + m.aggregations.avg, 0) / groupMetrics.length;\n\n return row;\n });\n\n return {\n columns,\n rows: rows.sort((a, b) => b.sum - a.sum) // 합계 기준 내림차순 정렬\n };\n }\n\n private generateGaugeWidgetData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n const value = this.generateMetricWidgetData(metrics, config).value;\n \n return {\n value,\n min: config.yAxis?.min || 0,\n max: config.yAxis?.max || 100,\n thresholds: [\n { value: 25, color: '#ef4444' },\n { value: 50, color: '#f59e0b' },\n { value: 75, color: '#10b981' },\n { value: 100, color: '#3b82f6' }\n ]\n };\n }\n\n private generateHeatmapWidgetData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n // 시간별 히트맵 데이터 생성 (시간 x 요일)\n const heatmapData: Array<{ x: number; y: number; value: number }> = [];\n \n for (const metric of metrics) {\n const hour = metric.timestamp.getHours();\n const dayOfWeek = metric.timestamp.getDay();\n \n heatmapData.push({\n x: hour,\n y: dayOfWeek,\n value: this.getAggregatedValue(metric, config.aggregation || 'sum')\n });\n }\n\n return {\n data: heatmapData,\n xLabels: Array.from({ length: 24 }, (_, i) => `${i}:00`),\n yLabels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']\n };\n }\n\n private generateTrendWidgetData(metrics: AggregatedMetric[], config: VisualizationConfig): any {\n const sortedMetrics = metrics.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n \n if (sortedMetrics.length < 2) {\n return { trend: 'stable', change: 0, changePercent: 0 };\n }\n\n const values = sortedMetrics.map(m => this.getAggregatedValue(m, config.aggregation || 'avg'));\n const firstValue = values[0];\n const lastValue = values[values.length - 1];\n \n const change = lastValue - firstValue;\n const changePercent = firstValue !== 0 ? (change / firstValue) * 100 : 0;\n \n let trend: 'up' | 'down' | 'stable' = 'stable';\n if (Math.abs(changePercent) > 5) {\n trend = changePercent > 0 ? 'up' : 'down';\n }\n\n return {\n trend,\n change: Math.round(change * 100) / 100,\n changePercent: Math.round(changePercent * 100) / 100,\n values: values.slice(-10), // 최근 10개 값\n sparkline: values.map((value, index) => ({ x: index, y: value }))\n };\n }\n\n private filterMetrics(\n metrics: AggregatedMetric[],\n type: MetricType,\n filters: Record<string, any>\n ): AggregatedMetric[] {\n return metrics.filter(metric => {\n if (metric.type !== type) return false;\n \n for (const [key, value] of Object.entries(filters)) {\n if (Array.isArray(value)) {\n if (!value.includes(metric.dimensions[key])) return false;\n } else if (value && metric.dimensions[key] !== value) {\n return false;\n }\n }\n \n return true;\n });\n }\n\n private applyQueryFilters(\n metrics: AggregatedMetric[],\n query: AnalyticsQuery,\n filters: Record<string, any>\n ): AggregatedMetric[] {\n let filteredMetrics = metrics;\n\n // 메트릭 타입 필터\n if (query.metrics && query.metrics.length > 0) {\n filteredMetrics = filteredMetrics.filter(m => query.metrics.includes(m.type));\n }\n\n // 시간 범위 필터\n if (query.dateRange) {\n filteredMetrics = filteredMetrics.filter(m => \n m.timestamp >= query.dateRange.start && m.timestamp <= query.dateRange.end\n );\n }\n\n // 추가 필터 적용\n const combinedFilters = { ...query.filters, ...filters };\n for (const [key, value] of Object.entries(combinedFilters)) {\n if (value === undefined || value === null) continue;\n \n filteredMetrics = filteredMetrics.filter(m => {\n if (Array.isArray(value)) {\n return value.includes(m.dimensions[key]);\n }\n return m.dimensions[key] === value;\n });\n }\n\n return filteredMetrics;\n }\n\n private calculatePreviousPeriodValue(\n metrics: AggregatedMetric[],\n previousTimeRange: { start: Date; end: Date }\n ): number {\n // 이전 기간 데이터는 실제로는 별도로 조회해야 함\n // 여기서는 현재 데이터의 평균값으로 추정\n const currentValue = metrics.reduce((sum, m) => sum + m.aggregations.sum, 0);\n return currentValue * 0.9; // 10% 감소로 가정\n }\n\n private getAggregatedValue(metric: AggregatedMetric, aggregation: string): number {\n switch (aggregation) {\n case 'sum': return metric.aggregations.sum;\n case 'avg': return metric.aggregations.avg;\n case 'min': return metric.aggregations.min;\n case 'max': return metric.aggregations.max;\n case 'count': return metric.aggregations.count;\n default: return metric.aggregations.avg;\n }\n }\n\n private formatValue(value: number, config: VisualizationConfig): string {\n if (!isFinite(value)) return '0';\n \n if (value >= 1000000) {\n return `${(value / 1000000).toFixed(1)}M`;\n } else if (value >= 1000) {\n return `${(value / 1000).toFixed(1)}K`;\n }\n \n return value.toFixed(0);\n }\n\n private getDefaultColor(index: number): string {\n const colors = [\n '#3b82f6', '#ef4444', '#10b981', '#f59e0b',\n '#8b5cf6', '#06b6d4', '#f97316', '#84cc16'\n ];\n return colors[index % colors.length];\n }\n\n private initializeDefaultWidgets(): void {\n this.config.widgets = [\n {\n id: 'messages_sent_chart',\n type: 'chart',\n title: 'Messages Sent Over Time',\n position: { x: 0, y: 0, width: 6, height: 4 },\n query: {\n metrics: [MetricType.MESSAGE_SENT],\n dateRange: { start: new Date(), end: new Date() },\n interval: 'hour'\n },\n visualization: {\n chartType: 'line',\n aggregation: 'sum',\n showGrid: true\n }\n },\n {\n id: 'delivery_rate_gauge',\n type: 'gauge',\n title: 'Delivery Rate',\n position: { x: 6, y: 0, width: 3, height: 4 },\n query: {\n metrics: [MetricType.DELIVERY_RATE],\n dateRange: { start: new Date(), end: new Date() }\n },\n visualization: {\n aggregation: 'avg',\n yAxis: { min: 0, max: 100 }\n }\n },\n {\n id: 'provider_performance_table',\n type: 'table',\n title: 'Provider Performance',\n position: { x: 0, y: 4, width: 12, height: 4 },\n query: {\n metrics: [MetricType.MESSAGE_SENT, MetricType.MESSAGE_DELIVERED, MetricType.MESSAGE_FAILED],\n dateRange: { start: new Date(), end: new Date() }\n },\n visualization: {\n groupBy: ['provider'],\n aggregation: 'sum'\n }\n }\n ];\n }\n}","/**\n * Export Manager\n * 다양한 형식으로 데이터 내보내기\n */\n\nimport type { AnalyticsReport, AggregatedMetric, InsightData } from '../types/analytics.types';\n\nexport interface ExportConfig {\n formats: ExportFormat[];\n maxFileSize: number; // bytes\n compressionEnabled: boolean;\n watermark?: {\n text: string;\n position: 'top' | 'bottom';\n };\n scheduling?: {\n enabled: boolean;\n cron: string;\n recipients: string[];\n };\n}\n\nexport interface ExportFormat {\n type: 'csv' | 'excel' | 'pdf' | 'json' | 'xml';\n options?: Record<string, any>;\n template?: string;\n}\n\nexport interface ExportResult {\n id: string;\n format: string;\n fileName: string;\n filePath: string;\n fileSize: number;\n createdAt: Date;\n downloadUrl?: string;\n expiresAt?: Date;\n}\n\nexport interface CSVExportOptions {\n delimiter: string;\n includeHeaders: boolean;\n dateFormat: string;\n encoding: 'utf-8' | 'utf-16';\n}\n\nexport interface PDFExportOptions {\n orientation: 'portrait' | 'landscape';\n pageSize: 'A4' | 'A3' | 'letter';\n includeCharts: boolean;\n template: 'standard' | 'executive' | 'detailed';\n}\n\nexport interface ExcelExportOptions {\n worksheets: Array<{\n name: string;\n data: any[];\n charts?: Array<{\n type: 'line' | 'bar' | 'pie';\n title: string;\n range: string;\n }>;\n }>;\n formatting: {\n autoFilter: boolean;\n freezeFirstRow: boolean;\n columnWidths: 'auto' | number[];\n };\n}\n\nexport class ExportManager {\n private config: ExportConfig;\n private exports: Map<string, ExportResult> = new Map();\n private exportQueue: Array<{ id: string; priority: number; data: any }> = [];\n\n private defaultConfig: ExportConfig = {\n formats: [\n { type: 'csv', options: { delimiter: ',', includeHeaders: true } },\n { type: 'json' },\n { type: 'pdf', options: { orientation: 'portrait', pageSize: 'A4' } }\n ],\n maxFileSize: 50 * 1024 * 1024, // 50MB\n compressionEnabled: true\n };\n\n constructor(config: Partial<ExportConfig> = {}) {\n this.config = { ...this.defaultConfig, ...config };\n }\n\n /**\n * 분석 보고서 내보내기\n */\n async exportReport(\n report: AnalyticsReport,\n format: ExportFormat,\n options: any = {}\n ): Promise<ExportResult> {\n const exportId = this.generateExportId();\n \n try {\n let result: ExportResult;\n\n switch (format.type) {\n case 'csv':\n result = await this.exportToCSV(report, { ...format.options, ...options }, exportId);\n break;\n case 'excel':\n result = await this.exportToExcel(report, { ...format.options, ...options }, exportId);\n break;\n case 'pdf':\n result = await this.exportToPDF(report, { ...format.options, ...options }, exportId);\n break;\n case 'json':\n result = await this.exportToJSON(report, { ...format.options, ...options }, exportId);\n break;\n case 'xml':\n result = await this.exportToXML(report, { ...format.options, ...options }, exportId);\n break;\n default:\n throw new Error(`Unsupported export format: ${format.type}`);\n }\n\n this.exports.set(exportId, result);\n return result;\n\n } catch (error) {\n throw new Error(`Export failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * 메트릭 데이터 내보내기\n */\n async exportMetrics(\n metrics: AggregatedMetric[],\n format: ExportFormat,\n options: any = {}\n ): Promise<ExportResult> {\n const exportId = this.generateExportId();\n\n try {\n let result: ExportResult;\n\n switch (format.type) {\n case 'csv':\n result = await this.exportMetricsToCSV(metrics, { ...format.options, ...options }, exportId);\n break;\n case 'json':\n result = await this.exportMetricsToJSON(metrics, { ...format.options, ...options }, exportId);\n break;\n default:\n throw new Error(`Metrics export not supported for format: ${format.type}`);\n }\n\n this.exports.set(exportId, result);\n return result;\n\n } catch (error) {\n throw new Error(`Metrics export failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * 인사이트 데이터 내보내기\n */\n async exportInsights(\n insights: InsightData[],\n format: ExportFormat,\n options: any = {}\n ): Promise<ExportResult> {\n const exportId = this.generateExportId();\n\n try {\n let result: ExportResult;\n\n switch (format.type) {\n case 'csv':\n result = await this.exportInsightsToCSV(insights, { ...format.options, ...options }, exportId);\n break;\n case 'json':\n result = await this.exportInsightsToJSON(insights, { ...format.options, ...options }, exportId);\n break;\n case 'pdf':\n result = await this.exportInsightsToPDF(insights, { ...format.options, ...options }, exportId);\n break;\n default:\n throw new Error(`Insights export not supported for format: ${format.type}`);\n }\n\n this.exports.set(exportId, result);\n return result;\n\n } catch (error) {\n throw new Error(`Insights export failed: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n /**\n * 내보내기 상태 조회\n */\n getExportStatus(exportId: string): ExportResult | null {\n return this.exports.get(exportId) || null;\n }\n\n /**\n * 내보내기 목록 조회\n */\n listExports(limit: number = 50): ExportResult[] {\n const exports = Array.from(this.exports.values())\n .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n \n return exports.slice(0, limit);\n }\n\n /**\n * 내보내기 삭제\n */\n deleteExport(exportId: string): boolean {\n return this.exports.delete(exportId);\n }\n\n private async exportToCSV(report: AnalyticsReport, options: CSVExportOptions, exportId: string): Promise<ExportResult> {\n const delimiter = options.delimiter || ',';\n const dateFormat = options.dateFormat || 'yyyy-MM-dd HH:mm:ss';\n \n let csvContent = '';\n\n // 헤더\n if (options.includeHeaders) {\n csvContent += ['Metric Type', 'Value', 'Change', 'Trend', 'Breakdown'].join(delimiter) + '\\n';\n }\n\n // 데이터 행\n for (const metric of report.metrics) {\n const row = [\n metric.type.toString(),\n metric.value.toString(),\n (metric.change || 0).toString(),\n metric.trend || 'stable',\n metric.breakdown ? JSON.stringify(metric.breakdown) : ''\n ];\n\n csvContent += row.map(cell => `\"${cell}\"`).join(delimiter) + '\\n';\n }\n\n const fileName = `report_${report.id}_${Date.now()}.csv`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(csvContent, 'utf8');\n\n // 파일 크기 확인\n if (fileSize > this.config.maxFileSize) {\n throw new Error(`File size ${fileSize} exceeds maximum ${this.config.maxFileSize}`);\n }\n\n // 실제 구현에서는 파일 시스템에 저장\n console.log(`Saving CSV to ${filePath}, size: ${fileSize} bytes`);\n\n return {\n id: exportId,\n format: 'csv',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24시간\n };\n }\n\n private async exportToExcel(report: AnalyticsReport, options: ExcelExportOptions, exportId: string): Promise<ExportResult> {\n // Excel 생성 로직 (실제로는 ExcelJS 등 라이브러리 사용)\n const worksheetData = {\n name: 'Report',\n data: report.metrics.map(metric => ({\n 'Metric Type': metric.type,\n 'Value': metric.value,\n 'Change': metric.change || 0,\n 'Trend': metric.trend || 'stable'\n }))\n };\n\n const fileName = `report_${report.id}_${Date.now()}.xlsx`;\n const filePath = `/exports/${fileName}`;\n const fileSize = 10240; // 추정 크기\n\n console.log(`Generating Excel file: ${fileName}`);\n\n return {\n id: exportId,\n format: 'excel',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportToPDF(report: AnalyticsReport, options: PDFExportOptions, exportId: string): Promise<ExportResult> {\n // PDF 생성 로직 (실제로는 PDFKit 등 라이브러리 사용)\n const template = options.template || 'standard';\n const orientation = options.orientation || 'portrait';\n \n console.log(`Generating PDF report with template: ${template}, orientation: ${orientation}`);\n\n const fileName = `report_${report.id}_${Date.now()}.pdf`;\n const filePath = `/exports/${fileName}`;\n const fileSize = 25600; // 추정 크기\n\n return {\n id: exportId,\n format: 'pdf',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportToJSON(report: AnalyticsReport, options: any, exportId: string): Promise<ExportResult> {\n const jsonContent = JSON.stringify(report, null, 2);\n const fileName = `report_${report.id}_${Date.now()}.json`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(jsonContent, 'utf8');\n\n return {\n id: exportId,\n format: 'json',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportToXML(report: AnalyticsReport, options: any, exportId: string): Promise<ExportResult> {\n // XML 생성 로직\n let xmlContent = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n';\n xmlContent += '<report>\\n';\n xmlContent += ` <id>${report.id}</id>\\n`;\n xmlContent += ` <name>${report.name}</name>\\n`;\n xmlContent += ` <generatedAt>${report.generatedAt.toISOString()}</generatedAt>\\n`;\n xmlContent += ' <metrics>\\n';\n\n for (const metric of report.metrics) {\n xmlContent += ' <metric>\\n';\n xmlContent += ` <type>${metric.type}</type>\\n`;\n xmlContent += ` <value>${metric.value}</value>\\n`;\n xmlContent += ` <change>${metric.change || 0}</change>\\n`;\n xmlContent += ` <trend>${metric.trend || 'stable'}</trend>\\n`;\n xmlContent += ' </metric>\\n';\n }\n\n xmlContent += ' </metrics>\\n';\n xmlContent += '</report>\\n';\n\n const fileName = `report_${report.id}_${Date.now()}.xml`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(xmlContent, 'utf8');\n\n return {\n id: exportId,\n format: 'xml',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportMetricsToCSV(metrics: AggregatedMetric[], options: CSVExportOptions, exportId: string): Promise<ExportResult> {\n const delimiter = options.delimiter || ',';\n let csvContent = '';\n\n // 헤더\n if (options.includeHeaders) {\n csvContent += [\n 'Timestamp', 'Type', 'Interval', 'Count', 'Sum', 'Average', 'Min', 'Max', 'Dimensions'\n ].join(delimiter) + '\\n';\n }\n\n // 데이터\n for (const metric of metrics) {\n const row = [\n metric.timestamp.toISOString(),\n metric.type.toString(),\n metric.interval,\n metric.aggregations.count.toString(),\n metric.aggregations.sum.toString(),\n metric.aggregations.avg.toString(),\n metric.aggregations.min.toString(),\n metric.aggregations.max.toString(),\n JSON.stringify(metric.dimensions)\n ];\n\n csvContent += row.map(cell => `\"${cell}\"`).join(delimiter) + '\\n';\n }\n\n const fileName = `metrics_${Date.now()}.csv`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(csvContent, 'utf8');\n\n return {\n id: exportId,\n format: 'csv',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportMetricsToJSON(metrics: AggregatedMetric[], options: any, exportId: string): Promise<ExportResult> {\n const jsonContent = JSON.stringify({\n exportedAt: new Date().toISOString(),\n count: metrics.length,\n metrics\n }, null, 2);\n\n const fileName = `metrics_${Date.now()}.json`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(jsonContent, 'utf8');\n\n return {\n id: exportId,\n format: 'json',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportInsightsToCSV(insights: InsightData[], options: CSVExportOptions, exportId: string): Promise<ExportResult> {\n const delimiter = options.delimiter || ',';\n let csvContent = '';\n\n // 헤더\n if (options.includeHeaders) {\n csvContent += [\n 'ID', 'Type', 'Title', 'Description', 'Severity', 'Metric', 'Value', 'Confidence', 'Detected At', 'Recommendations'\n ].join(delimiter) + '\\n';\n }\n\n // 데이터\n for (const insight of insights) {\n const row = [\n insight.id,\n insight.type,\n insight.title,\n insight.description,\n insight.severity,\n insight.metric.toString(),\n insight.value.toString(),\n insight.confidence.toString(),\n insight.detectedAt.toISOString(),\n (insight.recommendations || []).join('; ')\n ];\n\n csvContent += row.map(cell => `\"${cell}\"`).join(delimiter) + '\\n';\n }\n\n const fileName = `insights_${Date.now()}.csv`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(csvContent, 'utf8');\n\n return {\n id: exportId,\n format: 'csv',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportInsightsToJSON(insights: InsightData[], options: any, exportId: string): Promise<ExportResult> {\n const jsonContent = JSON.stringify({\n exportedAt: new Date().toISOString(),\n count: insights.length,\n insights\n }, null, 2);\n\n const fileName = `insights_${Date.now()}.json`;\n const filePath = `/exports/${fileName}`;\n const fileSize = Buffer.byteLength(jsonContent, 'utf8');\n\n return {\n id: exportId,\n format: 'json',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private async exportInsightsToPDF(insights: InsightData[], options: PDFExportOptions, exportId: string): Promise<ExportResult> {\n // PDF 생성 로직 (인사이트 특화)\n console.log(`Generating insights PDF with ${insights.length} insights`);\n\n const fileName = `insights_${Date.now()}.pdf`;\n const filePath = `/exports/${fileName}`;\n const fileSize = 15360; // 추정 크기\n\n return {\n id: exportId,\n format: 'pdf',\n fileName,\n filePath,\n fileSize,\n createdAt: new Date(),\n downloadUrl: `/api/exports/${exportId}/download`,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000)\n };\n }\n\n private generateExportId(): string {\n return `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA,SAAuB,CAAC;AAAA,EACxB,YAAY;AAAA,EACZ,gBAAgB;AAAA;AAAA,EAChB,UAAqC,oBAAI,IAAI;AAAA,EAErD,YAAY,QAAyB;AACnC,SAAK,SAAS;AACd,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,QAAmC;AAE/C,SAAK,eAAe,MAAM;AAG1B,SAAK,OAAO,KAAK,MAAM;AAGvB,QAAI,KAAK,OAAO,UAAU,KAAK,WAAW;AACxC,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,SAAsC;AACvD,eAAW,UAAU,SAAS;AAC5B,YAAM,KAAK,QAAQ,MAAM;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,OAAqB,YAA2C;AACrF,UAAM,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,UAAU;AAC/C,UAAM,SAAuB,CAAC;AAE9B,eAAW,QAAQ,OAAO;AACxB,YAAM,UAAU,KAAK,SAAS;AAC9B,YAAM,UAAU,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC;AAE9C,YAAM,gBAAgB,QAAQ,OAAO,OAAK,EAAE,aAAa,MAAM;AAC/D,aAAO,KAAK,GAAG,aAAa;AAAA,IAC9B;AAEA,WAAO,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,MAAkB,WAAuC;AAC5E,UAAM,UAAU,KAAK,SAAS;AAC9B,UAAM,UAAU,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC;AAE9C,UAAM,WAAW,QAAQ;AAAA,MAAO,OAC9B,EAAE,aAAa,UAAU,SAAS,EAAE,aAAa,UAAU;AAAA,IAC7D;AAEA,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AAAA,IACF;AAEA,UAAM,SAAS,SAAS,IAAI,OAAK,EAAE,KAAK;AACxC,UAAM,MAAM,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,KAAK,CAAC;AAEpD,WAAO;AAAA,MACL,OAAO,SAAS;AAAA,MAChB;AAAA,MACA,KAAK,MAAM,SAAS;AAAA,MACpB,KAAK,KAAK,IAAI,GAAG,MAAM;AAAA,MACvB,KAAK,KAAK,IAAI,GAAG,MAAM;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,MAAkB,YAAoC,QAAQ,GAAkB;AACrG,UAAM,SAAqB;AAAA,MACzB,IAAI,KAAK,iBAAiB;AAAA,MAC1B;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,MAAkB,YAAoC,OAA8B;AACjG,UAAM,SAAqB;AAAA,MACzB,IAAI,KAAK,iBAAiB;AAAA,MAC1B;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,MAAkB,YAAoC,OAA8B;AACxG,UAAM,SAAqB;AAAA,MACzB,IAAI,KAAK,iBAAiB;AAAA,MAC1B;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA,UAAU;AAAA,QACR,aAAa;AAAA,MACf;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,OAAO,WAAW,GAAG;AAC5B;AAAA,IACF;AAEA,UAAM,UAAU,CAAC,GAAG,KAAK,MAAM;AAC/B,SAAK,SAAS,CAAC;AAEf,QAAI;AACF,YAAM,KAAK,eAAe,OAAO;AAAA,IACnC,SAAS,OAAO;AACd,cAAQ,MAAM,8BAA8B,KAAK;AAEjD,WAAK,OAAO,QAAQ,GAAG,OAAO;AAC9B,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,UAAM,SAAS,IAAI,KAAK,KAAK,IAAI,IAAK,KAAK,OAAO,gBAAgB,KAAK,KAAK,KAAK,GAAK;AAEtF,eAAW,CAAC,SAAS,OAAO,KAAK,KAAK,QAAQ,QAAQ,GAAG;AACvD,YAAM,WAAW,QAAQ,OAAO,OAAK,EAAE,aAAa,MAAM;AAC1D,WAAK,QAAQ,IAAI,SAAS,QAAQ;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,eAAe,QAA0B;AAC/C,QAAI,CAAC,OAAO,IAAI;AACd,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AAEA,QAAI,CAAC,OAAO,MAAM;AAChB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,QAAI,OAAO,OAAO,UAAU,YAAY,MAAM,OAAO,KAAK,GAAG;AAC3D,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,QAAI,CAAC,OAAO,aAAa,EAAE,OAAO,qBAAqB,OAAO;AAC5D,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,QAAI,CAAC,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC/D,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,SAAsC;AAEjE,UAAM,UAAU,oBAAI,IAA0B;AAE9C,eAAW,UAAU,SAAS;AAC5B,YAAM,UAAU,OAAO,KAAK,SAAS;AACrC,UAAI,CAAC,QAAQ,IAAI,OAAO,GAAG;AACzB,gBAAQ,IAAI,SAAS,CAAC,CAAC;AAAA,MACzB;AACA,cAAQ,IAAI,OAAO,EAAG,KAAK,MAAM;AAAA,IACnC;AAGA,eAAW,CAAC,SAAS,WAAW,KAAK,QAAQ,QAAQ,GAAG;AACtD,YAAM,WAAW,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC;AAC/C,eAAS,KAAK,GAAG,WAAW;AAG5B,eAAS,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAGrE,UAAI,SAAS,SAAS,KAAO;AAC3B,iBAAS,OAAO,GAAK;AAAA,MACvB;AAEA,WAAK,QAAQ,IAAI,SAAS,QAAQ;AAAA,IACpC;AAEA,YAAQ,IAAI,aAAa,QAAQ,MAAM,UAAU;AAAA,EACnD;AAAA,EAEQ,sBAA4B;AAClC,gBAAY,YAAY;AACtB,UAAI;AACF,cAAM,KAAK,MAAM;AAAA,MACnB,SAAS,OAAO;AACd,gBAAQ,MAAM,4BAA4B,KAAK;AAAA,MACjD;AAAA,IACF,GAAG,KAAK,aAAa;AAGrB,gBAAY,YAAY;AACtB,UAAI;AACF,cAAM,KAAK,QAAQ;AAAA,MACrB,SAAS,OAAO;AACd,gBAAQ,MAAM,mBAAmB,KAAK;AAAA,MACxC;AAAA,IACF,GAAG,KAAK,KAAK,KAAK,GAAI;AAAA,EACxB;AAAA,EAEQ,mBAA2B;AACjC,WAAO,UAAU,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAAA,EACxE;AACF;;;ACtPA,iBAAkB;AASX,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,kBAAe;AACf,EAAAA,YAAA,uBAAoB;AACpB,EAAAA,YAAA,oBAAiB;AACjB,EAAAA,YAAA,qBAAkB;AAClB,EAAAA,YAAA,oBAAiB;AACjB,EAAAA,YAAA,0BAAuB;AACvB,EAAAA,YAAA,mBAAgB;AAChB,EAAAA,YAAA,gBAAa;AACb,EAAAA,YAAA,mBAAgB;AAChB,EAAAA,YAAA,gBAAa;AAVH,SAAAA;AAAA,GAAA;AAoGL,IAAM,mBAAmB,aAAE,OAAO;AAAA,EACvC,IAAI,aAAE,OAAO;AAAA,EACb,MAAM,aAAE,WAAW,UAAU;AAAA,EAC7B,WAAW,aAAE,KAAK;AAAA,EAClB,OAAO,aAAE,OAAO;AAAA,EAChB,YAAY,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,OAAO,CAAC;AAAA,EAC3C,UAAU,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC,EAAE,SAAS;AACnD,CAAC;AAEM,IAAM,uBAAuB,aAAE,OAAO;AAAA,EAC3C,SAAS,aAAE,MAAM,aAAE,WAAW,UAAU,CAAC;AAAA,EACzC,WAAW,aAAE,OAAO;AAAA,IAClB,OAAO,aAAE,KAAK;AAAA,IACd,KAAK,aAAE,KAAK;AAAA,EACd,CAAC;AAAA,EACD,UAAU,aAAE,KAAK,CAAC,UAAU,QAAQ,OAAO,QAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,EACtE,SAAS,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAChD,SAAS,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACtC,SAAS,aAAE,MAAM,aAAE,OAAO;AAAA,IACxB,OAAO,aAAE,OAAO;AAAA,IAChB,WAAW,aAAE,KAAK,CAAC,OAAO,MAAM,CAAC;AAAA,EACnC,CAAC,CAAC,EAAE,SAAS;AAAA,EACb,OAAO,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAK,EAAE,SAAS;AAAA,EAC7C,QAAQ,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACrC,CAAC;AAEM,IAAM,oBAAoB,aAAE,OAAO;AAAA,EACxC,IAAI,aAAE,OAAO;AAAA,EACb,MAAM,aAAE,KAAK,CAAC,WAAW,SAAS,gBAAgB,CAAC;AAAA,EACnD,OAAO,aAAE,OAAO;AAAA,EAChB,aAAa,aAAE,OAAO;AAAA,EACtB,UAAU,aAAE,KAAK,CAAC,OAAO,UAAU,QAAQ,UAAU,CAAC;AAAA,EACtD,QAAQ,aAAE,WAAW,UAAU;AAAA,EAC/B,YAAY,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,OAAO,CAAC;AAAA,EAC3C,OAAO,aAAE,OAAO;AAAA,EAChB,eAAe,aAAE,OAAO,EAAE,SAAS;AAAA,EACnC,YAAY,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,EACnC,YAAY,aAAE,QAAQ;AAAA,EACtB,iBAAiB,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC9C,YAAY,aAAE,KAAK;AACrB,CAAC;;;AC7IM,IAAM,kBAAN,MAAsB;AAAA,EACnB;AAAA,EAER,YAAY,QAAyB;AACnC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAqB,MAAsC;AAC/D,UAAM,aAAa,IAAI,KAAK,IAAI;AAChC,eAAW,SAAS,GAAG,GAAG,GAAG,CAAC;AAE9B,UAAM,WAAW,IAAI,KAAK,IAAI;AAC9B,aAAS,SAAS,IAAI,IAAI,IAAI,GAAG;AAEjC,UAAM,cAAc,IAAI,KAAK,UAAU;AACvC,gBAAY,QAAQ,YAAY,QAAQ,IAAI,CAAC;AAE7C,WAAO,KAAK,eAAe;AAAA,MACzB,IAAI,SAAS,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MAC7C,MAAM,mBAAmB,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MACzD,aAAa;AAAA,MACb,WAAW,EAAE,OAAO,YAAY,KAAK,SAAS;AAAA,MAC9C,SAAS,CAAC;AAAA,MACV,SAAS,MAAM,KAAK,sBAAsB,YAAY,UAAU,WAAW;AAAA,MAC3E,aAAa,oBAAI,KAAK;AAAA,MACtB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAqB,eAA+C;AACxE,UAAM,UAAU,IAAI,KAAK,aAAa;AACtC,YAAQ,QAAQ,QAAQ,QAAQ,IAAI,CAAC;AAErC,UAAM,oBAAoB,IAAI,KAAK,aAAa;AAChD,sBAAkB,QAAQ,kBAAkB,QAAQ,IAAI,CAAC;AAEzD,WAAO,KAAK,eAAe;AAAA,MACzB,IAAI,UAAU,cAAc,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MACvD,MAAM,2BAA2B,cAAc,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MAC1E,aAAa;AAAA,MACb,WAAW,EAAE,OAAO,eAAe,KAAK,QAAQ;AAAA,MAChD,SAAS,CAAC;AAAA,MACV,SAAS,MAAM,KAAK,uBAAuB,eAAe,SAAS,iBAAiB;AAAA,MACpF,aAAa,oBAAI,KAAK;AAAA,MACtB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,MAAc,OAAyC;AACjF,UAAM,aAAa,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;AAC9C,UAAM,WAAW,IAAI,KAAK,MAAM,OAAO,CAAC;AAExC,UAAM,qBAAqB,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;AACtD,UAAM,mBAAmB,IAAI,KAAK,MAAM,QAAQ,GAAG,CAAC;AAEpD,WAAO,KAAK,eAAe;AAAA,MACzB,IAAI,WAAW,IAAI,IAAI,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,MACxD,MAAM,oBAAoB,IAAI,IAAI,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,MACnE,aAAa;AAAA,MACb,WAAW,EAAE,OAAO,YAAY,KAAK,SAAS;AAAA,MAC9C,SAAS,CAAC;AAAA,MACV,SAAS,MAAM,KAAK,wBAAwB,YAAY,UAAU,oBAAoB,gBAAgB;AAAA,MACtG,aAAa,oBAAI,KAAK;AAAA,MACtB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,uBAAuB,YAAoB,WAAiE;AAChH,WAAO,KAAK,eAAe;AAAA,MACzB,IAAI,YAAY,UAAU,IAAI,UAAU,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MACzE,MAAM,0BAA0B,UAAU;AAAA,MAC1C,aAAa,qCAAqC,UAAU;AAAA,MAC5D;AAAA,MACA,SAAS,EAAE,UAAU,WAAW;AAAA,MAChC,SAAS,MAAM,KAAK,yBAAyB,YAAY,SAAS;AAAA,MAClE,aAAa,oBAAI,KAAK;AAAA,MACtB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,4BAA4B,WAAiE;AACjG,WAAO,KAAK,eAAe;AAAA,MACzB,IAAI,kBAAkB,UAAU,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA,MACjE,MAAM;AAAA,MACN,aAAa;AAAA,MACb;AAAA,MACA,SAAS,CAAC;AAAA,MACV,SAAS,MAAM,KAAK,yBAAyB,SAAS;AAAA,MACtD,aAAa,oBAAI,KAAK;AAAA,MACtB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBACJ,MACA,WACA,SACA,aAC0B;AAC1B,WAAO,KAAK,eAAe;AAAA,MACzB,IAAI,UAAU,KAAK,IAAI,CAAC;AAAA,MACxB;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,SAAS,MAAM,KAAK,uBAAuB,WAAW,SAAS,WAAW;AAAA,MAC1E,aAAa,oBAAI,KAAK;AAAA,MACtB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,QAA0C;AAC1D,UAAM,UAAU,CAAC,eAAe,SAAS,cAAc,OAAO;AAC9D,UAAM,OAAO,OAAO,QAAQ,IAAI,YAAU;AAAA,MACxC,OAAO,KAAK,SAAS;AAAA,MACrB,OAAO,MAAM,SAAS;AAAA,MACtB,OAAO,QAAQ,QAAQ,CAAC,KAAK;AAAA,MAC7B,OAAO,SAAS;AAAA,IAClB,CAAC;AAED,UAAM,aAAa,CAAC,SAAS,GAAG,IAAI,EACjC,IAAI,SAAO,IAAI,IAAI,UAAQ,IAAI,IAAI,GAAG,EAAE,KAAK,GAAG,CAAC,EACjD,KAAK,IAAI;AAEZ,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,QAA0C;AAC3D,WAAO,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,EACvC;AAAA,EAEA,MAAc,eAAe,YAAuD;AAElF,SAAK,eAAe,UAAU;AAG9B,eAAW,QAAQ,KAAK,CAAC,GAAG,MAAM;AAChC,YAAM,WAAW,KAAK,kBAAkB,EAAE,IAAI,IAAI,KAAK,kBAAkB,EAAE,IAAI;AAC/E,aAAO;AAAA,IACT,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBACZ,WACA,SACA,cACyB;AACzB,UAAM,UAA0B,CAAC;AAGjC,UAAM,YAAY,MAAM,KAAK,kDAAwC,WAAW,OAAO;AACvF,UAAM,iBAAiB,MAAM,KAAK,4DAA6C,WAAW,OAAO;AACjG,UAAM,cAAc,MAAM,KAAK,sDAA0C,WAAW,OAAO;AAC3F,UAAM,eAAe,MAAM,KAAK,wDAA2C,WAAW,OAAO;AAG7F,UAAM,gBAAgB,IAAI,KAAK,YAAY;AAC3C,UAAM,cAAc,IAAI,KAAK,YAAY;AACzC,gBAAY,SAAS,IAAI,IAAI,IAAI,GAAG;AAEpC,UAAM,WAAW,MAAM,KAAK,kDAAwC,eAAe,WAAW;AAE9F,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,OAAO;AAAA,MACP,QAAQ,KAAK,gBAAgB,WAAW,QAAQ;AAAA,MAChD,OAAO,KAAK,eAAe,WAAW,QAAQ;AAAA,IAChD,CAAC;AAED,QAAI,YAAY,GAAG;AACjB,YAAM,eAAgB,iBAAiB,YAAa;AACpD,YAAM,YAAa,cAAc,YAAa;AAE9C,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAED,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,QAAI,iBAAiB,GAAG;AACtB,YAAM,YAAa,eAAe,iBAAkB;AAEpD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,uBACZ,WACA,SACA,mBACyB;AAEzB,WAAO,KAAK,sBAAsB,WAAW,SAAS,iBAAiB;AAAA,EACzE;AAAA,EAEA,MAAc,wBACZ,YACA,UACA,oBACA,kBACyB;AAEzB,WAAO,KAAK,sBAAsB,YAAY,UAAU,kBAAkB;AAAA,EAC5E;AAAA,EAEA,MAAc,yBACZ,YACA,WACyB;AACzB,UAAM,UAA0B,CAAC;AAGjC,UAAM,cAAc,MAAM,KAAK,uBAAuB,YAAY,SAAS;AAE3E,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,OAAO,YAAY;AAAA,MACnB,WAAW;AAAA,QACT,gBAAgB,YAAY;AAAA,QAC5B,cAAc,YAAY;AAAA,QAC1B,qBAAqB,YAAY;AAAA,MACnC;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,yBACZ,WACyB;AACzB,UAAM,UAA0B,CAAC;AAGjC,UAAM,gBAAgB,MAAM,KAAK,iBAAiB,SAAS;AAE3D,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,OAAO,cAAc;AAAA,MACrB,WAAW,cAAc;AAAA,IAC3B,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,uBACZ,WACA,SACA,aACyB;AACzB,UAAM,UAA0B,CAAC;AAEjC,eAAW,QAAQ,aAAa;AAC9B,YAAM,QAAQ,MAAM,KAAK,eAAe,MAAM,UAAU,OAAO,UAAU,KAAK,OAAO;AAErF,cAAQ,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ;AAAA;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eACZ,MACA,OACA,KACA,SACiB;AAGjB,WAAO,KAAK,MAAM,KAAK,OAAO,IAAI,GAAK;AAAA,EACzC;AAAA,EAEA,MAAc,uBAAuB,YAAoB,WAAuC;AAC9F,WAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW;AAAA,MACX,qBAAqB;AAAA;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,WAAuC;AACpE,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,QACV,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,gBAAgB;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,SAAiB,UAA0B;AACjE,QAAI,aAAa,EAAG,QAAO,UAAU,IAAI,MAAM;AAC/C,YAAS,UAAU,YAAY,WAAY;AAAA,EAC7C;AAAA,EAEQ,eAAe,SAAiB,UAA4C;AAClF,UAAM,SAAS,KAAK,gBAAgB,SAAS,QAAQ;AACrD,QAAI,KAAK,IAAI,MAAM,IAAI,EAAG,QAAO;AACjC,WAAO,SAAS,IAAI,OAAO;AAAA,EAC7B;AAAA,EAEQ,kBAAkB,MAA0B;AAClD,UAAM,aAAa;AAAA,MACjB,kCAAwB,GAAG;AAAA,MAC3B,oCAAyB,GAAG;AAAA,MAC5B,8BAAsB,GAAG;AAAA,MACzB,8BAAsB,GAAG;AAAA,MACzB,4CAA6B,GAAG;AAAA,MAChC,sCAA0B,GAAG;AAAA,MAC7B,wCAA2B,GAAG;AAAA,MAC9B,sCAA0B,GAAG;AAAA,MAC7B,kDAAgC,GAAG;AAAA,MACnC,oCAAyB,GAAG;AAAA,IAC9B;AAEA,WAAO,WAAW,IAAI,KAAK;AAAA,EAC7B;AAAA,EAEQ,eAAe,QAA+B;AACpD,QAAI,CAAC,OAAO,IAAI;AACd,YAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC;AAEA,QAAI,CAAC,OAAO,MAAM;AAChB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,QAAI,CAAC,OAAO,aAAa,CAAC,OAAO,UAAU,SAAS,CAAC,OAAO,UAAU,KAAK;AACzE,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI,OAAO,UAAU,SAAS,OAAO,UAAU,KAAK;AAClD,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAAA,EACF;AACF;;;AC9XO,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EACA,oBAAmF,oBAAI,IAAI;AAAA,EAC3F,iBAA4C,oBAAI,IAAI;AAAA,EAE5D,YAAY,QAAyB;AACnC,SAAK,SAAS;AACd,SAAK,oBAAoB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,QAA4C;AACxE,UAAM,WAA0B,CAAC;AACjC,UAAM,YAAY,KAAK,kBAAkB,IAAI,OAAO,IAAI;AAExD,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,QAAQ,UAAU,OAAO,OAAO,QAAQ,UAAU,KAAK;AAChE,eAAS,KAAK;AAAA,QACZ,IAAI,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAAA,QACpE,MAAM;AAAA,QACN,OAAO,GAAG,OAAO,IAAI;AAAA,QACrB,aAAa,SAAS,OAAO,KAAK,6BAA6B,UAAU,GAAG,KAAK,UAAU,GAAG;AAAA,QAC9F,UAAU,KAAK,kBAAkB,OAAO,OAAO,SAAS;AAAA,QACxD,QAAQ,OAAO;AAAA,QACf,YAAY,OAAO;AAAA,QACnB,OAAO,OAAO;AAAA,QACd,gBAAgB,UAAU,MAAM,UAAU,OAAO;AAAA,QACjD,YAAY,KAAK,oBAAoB,OAAO,OAAO,SAAS;AAAA,QAC5D,YAAY;AAAA,QACZ,iBAAiB,KAAK,wBAAwB,MAAM;AAAA,QACpD,YAAY,oBAAI,KAAK;AAAA,MACvB,CAAC;AAAA,IACH;AAGA,UAAM,cAAc,MAAM,KAAK,kBAAkB,MAAM;AACvD,QAAI,aAAa;AACf,eAAS,KAAK,WAAW;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,YAAwB,WAA+D;AAC3G,UAAM,WAA0B,CAAC;AAGjC,UAAM,oBAAoB,MAAM,KAAK,wBAAwB,YAAY,SAAS;AAClF,aAAS,KAAK,GAAG,iBAAiB;AAGlC,UAAM,mBAAmB,MAAM,KAAK,uBAAuB,YAAY,SAAS;AAChF,aAAS,KAAK,GAAG,gBAAgB;AAGjC,UAAM,qBAAqB,MAAM,KAAK,yBAAyB,YAAY,SAAS;AACpF,aAAS,KAAK,GAAG,kBAAkB;AAEnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,OAAuB,MAAkD;AAC9F,UAAM,WAA0B,CAAC;AAGjC,UAAM,sBAAsB,MAAM,KAAK,4BAA4B,IAAI;AACvE,aAAS,KAAK,GAAG,mBAAmB;AAGpC,UAAM,gBAAgB,MAAM,KAAK,sBAAsB,IAAI;AAC3D,aAAS,KAAK,GAAG,aAAa;AAG9B,UAAM,qBAAqB,MAAM,KAAK,2BAA2B,IAAI;AACrE,aAAS,KAAK,GAAG,kBAAkB;AAGnC,UAAM,yBAAyB,MAAM,KAAK,+BAA+B,IAAI;AAC7E,aAAS,KAAK,GAAG,sBAAsB;AAEvC,WAAO,SAAS,KAAK,CAAC,GAAG,MAAM;AAE7B,YAAM,gBAAgB,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,EAAE;AAChE,aAAO,cAAc,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ;AAAA,IAC7D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,YAAwB,cAAwF;AAElI,UAAM,aAAa,KAAK,eAAe,IAAI,UAAU,KAAK,CAAC;AAC3D,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,cAAc,CAAC;AACrB,UAAM,YAAY,WAAW,WAAW,SAAS,CAAC;AAClD,UAAM,QAAQ,KAAK,eAAe,UAAU;AAE5C,aAAS,IAAI,GAAG,KAAK,cAAc,KAAK;AACtC,YAAM,iBAAiB,YAAa,QAAQ;AAC5C,YAAM,aAAa,KAAK,IAAI,KAAK,IAAK,IAAI,GAAI;AAE9C,kBAAY,KAAK;AAAA,QACf,MAAM,IAAI,KAAK,KAAK,IAAI,IAAK,IAAI,KAAK,KAAK,KAAK,GAAK;AAAA,QACrD,WAAW,KAAK,IAAI,GAAG,cAAc;AAAA,QACrC;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBAAqC;AAEjD,SAAK,kBAAkB,uCAA6B,EAAE,KAAK,GAAG,KAAK,KAAO,QAAQ,IAAI,CAAC;AACvF,SAAK,kBAAkB,iDAAkC,EAAE,KAAK,GAAG,KAAK,KAAO,QAAQ,IAAI,CAAC;AAC5F,SAAK,kBAAkB,2CAA+B,EAAE,KAAK,GAAG,KAAK,KAAM,QAAQ,IAAI,CAAC;AACxF,SAAK,kBAAkB,yCAA8B,EAAE,KAAK,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;AACpF,SAAK,kBAAkB,mCAA2B,EAAE,KAAK,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC;AAAA,EAClF;AAAA,EAEA,MAAc,kBAAkB,QAAiD;AAC/E,UAAM,UAAU,KAAK,eAAe,IAAI,OAAO,IAAI,KAAK,CAAC;AACzD,YAAQ,KAAK,OAAO,KAAK;AAGzB,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,MAAM;AAAA,IAChB;AAEA,SAAK,eAAe,IAAI,OAAO,MAAM,OAAO;AAE5C,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,SAAS,CAAC,IAAI,QAAQ,QAAQ,SAAS,CAAC,KAAK,QAAQ,QAAQ,SAAS,CAAC;AAE7G,QAAI,KAAK,IAAI,YAAY,IAAI,KAAK;AAChC,aAAO;AAAA,QACL,IAAI,SAAS,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAAA,QAClE,MAAM;AAAA,QACN,OAAO,UAAU,eAAe,IAAI,aAAa,UAAU,OAAO,OAAO,IAAI;AAAA,QAC7E,aAAa,GAAG,OAAO,IAAI,gBAAgB,eAAe,KAAK,QAAQ,CAAC,CAAC;AAAA,QACzE,UAAU,KAAK,IAAI,YAAY,IAAI,MAAM,SAAS;AAAA,QAClD,QAAQ,OAAO;AAAA,QACf,YAAY,OAAO;AAAA,QACnB,OAAO,OAAO;AAAA,QACd,eAAe,QAAQ,QAAQ,SAAS,CAAC;AAAA,QACzC,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,iBAAiB,CAAC,WAAW,OAAO,IAAI,YAAY,8CAA8C;AAAA,QAClG,YAAY,oBAAI,KAAK;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,wBAAwB,YAAwB,WAA+D;AAE3H,UAAM,WAA0B,CAAC;AAGjC,UAAM,YAAY,UAAU,MAAM,OAAO,MAAM,KAAK,UAAU,MAAM,OAAO,MAAM;AACjF,UAAM,cAAc,UAAU,MAAM,SAAS;AAG7C,QAAI,CAAC,cAAc,cAAc,KAAK,cAAc,KAAK;AAEvD,eAAS,KAAK;AAAA,QACZ,IAAI,YAAY,KAAK,IAAI,CAAC;AAAA,QAC1B,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa,QAAQ,UAAU;AAAA,QAC/B,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY,EAAE,WAAW,YAAY;AAAA,QACrC,OAAO;AAAA;AAAA,QACP,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,iBAAiB,CAAC,kDAAkD,qCAAqC;AAAA,QACzG,YAAY,oBAAI,KAAK;AAAA,MACvB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,uBAAuB,YAAwB,WAA+D;AAE1H,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAc,yBAAyB,YAAwB,WAA+D;AAE5H,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAc,4BAA4B,MAAkD;AAC1F,UAAM,WAA0B,CAAC;AAGjC,UAAM,kBAAkB,KAAK,OAAO,OAAK,EAAE,4CAAiC;AAC5E,eAAW,UAAU,iBAAiB;AACpC,UAAI,OAAO,aAAa,MAAM,IAAI;AAChC,iBAAS,KAAK;AAAA,UACZ,IAAI,wBAAwB,KAAK,IAAI,CAAC;AAAA,UACtC,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa,oBAAoB,OAAO,aAAa,IAAI,QAAQ,CAAC,CAAC;AAAA,UACnE,UAAU,OAAO,aAAa,MAAM,KAAK,SAAS;AAAA,UAClD;AAAA,UACA,YAAY,OAAO;AAAA,UACnB,OAAO,OAAO,aAAa;AAAA,UAC3B,eAAe;AAAA,UACf,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,iBAAiB;AAAA,YACf;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA,YAAY,oBAAI,KAAK;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBAAsB,MAAkD;AACpF,UAAM,WAA0B,CAAC;AAGjC,UAAM,cAAc,KAAK,OAAO,OAAK,EAAE,0CAAgC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAErI,QAAI,YAAY,UAAU,GAAG;AAC3B,YAAM,QAAQ,KAAK,eAAe,YAAY,IAAI,OAAK,EAAE,aAAa,GAAG,CAAC;AAE1E,UAAI,KAAK,IAAI,KAAK,IAAI,KAAK;AACzB,iBAAS,KAAK;AAAA,UACZ,IAAI,cAAc,KAAK,IAAI,CAAC;AAAA,UAC5B,MAAM;AAAA,UACN,OAAO,GAAG,QAAQ,IAAI,eAAe,YAAY;AAAA,UACjD,aAAa,qBAAqB,QAAQ,IAAI,eAAe,YAAY,qBAAqB,KAAK,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC;AAAA,UACxH,UAAU;AAAA,UACV;AAAA,UACA,YAAY,CAAC;AAAA,UACb,OAAO,YAAY,YAAY,SAAS,CAAC,EAAE,aAAa;AAAA,UACxD,YAAY;AAAA,UACZ,YAAY,QAAQ;AAAA;AAAA,UACpB,iBAAiB,QAAQ,OAAO;AAAA,YAC9B;AAAA,YACA;AAAA,UACF,IAAI;AAAA,UACJ,YAAY,oBAAI,KAAK;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,2BAA2B,MAAkD;AACzF,UAAM,WAA0B,CAAC;AAGjC,UAAM,sBAAsB,oBAAI,IAAiD;AAEjF,eAAW,UAAU,MAAM;AACzB,YAAM,WAAW,OAAO,WAAW;AACnC,UAAI,CAAC,SAAU;AAEf,UAAI,CAAC,oBAAoB,IAAI,QAAQ,GAAG;AACtC,4BAAoB,IAAI,UAAU,EAAE,WAAW,GAAG,MAAM,EAAE,CAAC;AAAA,MAC7D;AAEA,YAAM,QAAQ,oBAAoB,IAAI,QAAQ;AAC9C,UAAI,OAAO,4CAAkC;AAC3C,cAAM,QAAQ,OAAO,aAAa;AAAA,MACpC,WAAW,OAAO,sDAAuC;AACvD,cAAM,aAAa,OAAO,aAAa;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,YAAY,MAAM,KAAK,oBAAoB,QAAQ,CAAC;AAC1D,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,QAAQ,UAAU,IAAI,CAAC,CAAC,UAAU,KAAK,OAAO;AAAA,QAClD;AAAA,QACA,MAAM,MAAM,OAAO,IAAK,MAAM,YAAY,MAAM,OAAQ,MAAM;AAAA,QAC9D,QAAQ,MAAM;AAAA,MAChB,EAAE;AAEF,YAAM,UAAU,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM;AAClE,YAAM,kBAAkB,MAAM,OAAO,OAAK,EAAE,OAAO,UAAU,EAAE;AAE/D,iBAAW,YAAY,iBAAiB;AACtC,iBAAS,KAAK;AAAA,UACZ,IAAI,uBAAuB,SAAS,QAAQ,IAAI,KAAK,IAAI,CAAC;AAAA,UAC1D,MAAM;AAAA,UACN,OAAO,YAAY,SAAS,QAAQ;AAAA,UACpC,aAAa,GAAG,SAAS,QAAQ,mBAAmB,SAAS,KAAK,QAAQ,CAAC,CAAC,sCAAsC,QAAQ,QAAQ,CAAC,CAAC;AAAA,UACpI,UAAU,SAAS,OAAO,UAAU,KAAK,SAAS;AAAA,UAClD;AAAA,UACA,YAAY,EAAE,UAAU,SAAS,SAAS;AAAA,UAC1C,OAAO,SAAS;AAAA,UAChB,eAAe;AAAA,UACf,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,iBAAiB;AAAA,YACf,WAAW,SAAS,QAAQ;AAAA,YAC5B;AAAA,YACA;AAAA,UACF;AAAA,UACA,YAAY,oBAAI,KAAK;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,+BAA+B,MAAkD;AAC7F,UAAM,WAA0B,CAAC;AAGjC,UAAM,eAAe,oBAAI,IAAoB;AAC7C,eAAW,UAAU,MAAM;AACzB,UAAI,OAAO,8CAAmC;AAC5C,cAAM,UAAU,OAAO,WAAW;AAClC,YAAI,SAAS;AACX,uBAAa,IAAI,UAAU,aAAa,IAAI,OAAO,KAAK,KAAK,OAAO,aAAa,GAAG;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aAAa,MAAM,KAAK,aAAa,OAAO,CAAC,EAAE,OAAO,CAAC,KAAK,UAAU,MAAM,OAAO,CAAC;AAC1F,QAAI,aAAa,GAAG;AAClB,YAAM,WAAW,aAAa,IAAI,KAAK,KAAK;AAC5C,YAAM,gBAAgB,aAAa,IAAI,UAAU,KAAK;AAGtD,UAAI,WAAW,aAAa,OAAO,gBAAgB,GAAG;AACpD,iBAAS,KAAK;AAAA,UACZ,IAAI,0BAA0B,KAAK,IAAI,CAAC;AAAA,UACxC,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa,IAAK,WAAW,aAAc,KAAK,QAAQ,CAAC,CAAC;AAAA,UAC1D,UAAU;AAAA,UACV;AAAA,UACA,YAAY,EAAE,cAAc,OAAO;AAAA,UACnC,OAAO;AAAA,UACP,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,iBAAiB;AAAA,YACf;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA,YAAY,oBAAI,KAAK;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,OAAe,WAAiG;AACxI,UAAM,WAAW,KAAK;AAAA,MACpB,KAAK,IAAI,QAAQ,UAAU,GAAG,IAAI,UAAU;AAAA,MAC5C,KAAK,IAAI,QAAQ,UAAU,GAAG,IAAI,UAAU;AAAA,IAC9C;AAEA,QAAI,WAAW,EAAG,QAAO;AACzB,QAAI,WAAW,EAAG,QAAO;AACzB,QAAI,WAAW,EAAG,QAAO;AACzB,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,OAAe,WAAiE;AAC1G,UAAM,WAAW,KAAK;AAAA,MACpB,KAAK,IAAI,QAAQ,UAAU,GAAG,IAAI,UAAU;AAAA,MAC5C,KAAK,IAAI,QAAQ,UAAU,GAAG,IAAI,UAAU;AAAA,IAC9C;AAEA,WAAO,KAAK,IAAI,MAAM,MAAO,WAAW,IAAK;AAAA,EAC/C;AAAA,EAEQ,wBAAwB,QAA8B;AAC5D,UAAM,kBAA4B,CAAC;AAEnC,YAAQ,OAAO,MAAM;AAAA,MACnB;AACE,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AACE,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AACE,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AACE,wBAAgB;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,IACJ;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,QAA0B;AAC/C,QAAI,OAAO,SAAS,EAAG,QAAO;AAG9B,UAAM,IAAI,OAAO;AACjB,UAAM,IAAI,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC;AAC/C,UAAM,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACxC,UAAM,OAAO,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAC7C,UAAM,QAAQ,EAAE,OAAO,CAAC,KAAK,IAAI,MAAM,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC;AAC9D,UAAM,QAAQ,EAAE,OAAO,CAAC,KAAK,OAAO,MAAM,KAAK,IAAI,CAAC;AAEpD,UAAM,SAAS,IAAI,QAAQ,OAAO,SAAS,IAAI,QAAQ,OAAO;AAC9D,WAAO;AAAA,EACT;AACF;;;ACtcO,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAqC,oBAAI,IAAI;AAAA,EAC7C,oBAAqD,oBAAI,IAAI;AAAA,EAErE,YAAY,QAAyB;AACnC,SAAK,SAAS;AACd,SAAK,mBAAmB,IAAI,iBAAiB,MAAM;AACnD,SAAK,kBAAkB,IAAI,gBAAgB,MAAM;AACjD,SAAK,gBAAgB,IAAI,cAAc,MAAM;AAG7C,SAAK,sBAAsB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,QAAmC;AACrD,QAAI,CAAC,KAAK,OAAO,eAAe,SAAS,OAAO,IAAI,GAAG;AACrD;AAAA,IACF;AAEA,UAAM,KAAK,iBAAiB,QAAQ,MAAM;AAG1C,QAAI,KAAK,OAAO,wBAAwB;AACtC,YAAM,KAAK,sBAAsB,MAAM;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,OAAiD;AAC3D,UAAM,YAAY,KAAK,IAAI;AAG3B,SAAK,cAAc,KAAK;AAGxB,UAAM,OAAO,MAAM,KAAK,aAAa,KAAK;AAG1C,UAAM,WAAW,MAAM,KAAK,iBAAiB,OAAO,IAAI;AAExD,UAAM,gBAAgB,KAAK,IAAI,IAAI;AAEnC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACP,cAAc,KAAK;AAAA,QACnB,WAAW,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,cAAc,OAAiD;AAEpE,WAAO,MAAM;AACX,YAAM,UAAU,MAAM,KAAK,iBAAiB,iBAAiB,OAAO,GAAI;AACxE,iBAAW,UAAU,SAAS;AAC5B,cAAM;AAAA,MACR;AACA,YAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,GAAI,CAAC;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,WAAuC;AAC5D,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,iBAAiB;AAAA,MACrB,OAAO,IAAI,KAAK,UAAU,MAAM,QAAQ,KAAK,UAAU,IAAI,QAAQ,IAAI,UAAU,MAAM,QAAQ,EAAE;AAAA,MACjG,KAAK,UAAU;AAAA,IACjB;AAGA,UAAM,cAAc,MAAM,KAAK,MAAM;AAAA,MACnC,SAAS,KAAK,OAAO;AAAA,MACrB,WAAW;AAAA,MACX,UAAU,KAAK,mBAAmB,SAAS;AAAA,MAC3C,SAAS,CAAC,YAAY,SAAS;AAAA,IACjC,CAAC;AAGD,UAAM,eAAe,MAAM,KAAK,MAAM;AAAA,MACpC,SAAS,KAAK,OAAO;AAAA,MACrB,WAAW;AAAA,MACX,UAAU,KAAK,mBAAmB,SAAS;AAAA,MAC3C,SAAS,CAAC,YAAY,SAAS;AAAA,IACjC,CAAC;AAGD,UAAM,OAAO,KAAK,cAAc,YAAY,MAAM,aAAa,IAAI;AAEnE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS,YAAY;AAAA,MACrB,UAAU,YAAY;AAAA,MACtB,QAAQ,KAAK,gBAAgB,YAAY,MAAM,aAAa,IAAI;AAAA,IAClE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,YAAwB,WAA+D;AAC3G,WAAO,KAAK,cAAc,gBAAgB,YAAY,SAAS;AAAA,EACjE;AAAA,EAEA,MAAc,sBAAsB,QAAmC;AAErE,UAAM,YAAY,MAAM,KAAK,cAAc,wBAAwB,MAAM;AAEzE,QAAI,UAAU,SAAS,GAAG;AAExB,YAAM,KAAK,gBAAgB,SAAS;AAAA,IACtC;AAAA,EACF;AAAA,EAEQ,cAAc,OAA6B;AACjD,QAAI,MAAM,UAAU,SAAS,MAAM,UAAU,KAAK;AAChD,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,UAAM,aAAa,KAAK,KAAK,KAAK,KAAK;AACvC,QAAI,MAAM,UAAU,IAAI,QAAQ,IAAI,MAAM,UAAU,MAAM,QAAQ,IAAI,YAAY;AAChF,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,QAAI,MAAM,SAAS,MAAM,QAAQ,KAAO;AACtC,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,MAAc,aAAa,OAAoD;AAI7E,UAAM,WAAW,MAAM,YAAY,KAAK,mBAAmB,MAAM,SAAS;AAC1E,UAAM,WAAW,KAAK,iBAAiB,KAAK;AAG5C,QAAI,KAAK,kBAAkB,IAAI,QAAQ,GAAG;AACxC,aAAO,KAAK,kBAAkB,IAAI,QAAQ;AAAA,IAC5C;AAGA,UAAM,aAAa,MAAM,KAAK,mBAAmB,OAAO,QAAQ;AAGhE,SAAK,kBAAkB,IAAI,UAAU,UAAU;AAE/C,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,mBAAmB,OAAuB,UAA+C;AAGrG,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAc,iBAAiB,OAAuB,MAAkD;AACtG,WAAO,KAAK,cAAc,iBAAiB,OAAO,IAAI;AAAA,EACxD;AAAA,EAEQ,mBAAmB,WAAqF;AAC9G,UAAM,aAAa,UAAU,IAAI,QAAQ,IAAI,UAAU,MAAM,QAAQ;AACrE,UAAM,eAAe,cAAc,KAAK,KAAK,KAAK;AAElD,QAAI,gBAAgB,EAAG,QAAO;AAC9B,QAAI,gBAAgB,EAAG,QAAO;AAC9B,QAAI,gBAAgB,GAAI,QAAO;AAC/B,QAAI,gBAAgB,GAAI,QAAO;AAC/B,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,SAA6B,UAA8B;AAE/E,WAAO;AAAA,MACL,eAAe,KAAK,WAAW,SAAS,cAAc;AAAA,MACtD,cAAc,KAAK,cAAc,SAAS,qBAAqB,cAAc;AAAA,MAC7E,WAAW,KAAK,cAAc,SAAS,kBAAkB,cAAc;AAAA,MACvE,WAAW,KAAK,cAAc,SAAS,mBAAmB,mBAAmB;AAAA,IAC/E;AAAA,EACF;AAAA,EAEQ,gBAAgB,SAA6B,UAA8B;AAEjF,WAAO,CAAC;AAAA,EACV;AAAA,EAEQ,WAAW,SAA6B,MAAsB;AACpE,WAAO,QACJ,OAAO,OAAK,EAAE,KAAK,SAAS,MAAM,IAAI,EACtC,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAAA,EACnD;AAAA,EAEQ,cAAc,SAA6B,WAAmB,aAA6B;AACjG,UAAM,MAAM,KAAK,WAAW,SAAS,SAAS;AAC9C,UAAM,MAAM,KAAK,WAAW,SAAS,WAAW;AAChD,WAAO,MAAM,IAAK,MAAM,MAAO,MAAM;AAAA,EACvC;AAAA,EAEQ,iBAAiB,OAA+B;AACtD,WAAO,KAAK,UAAU;AAAA,MACpB,SAAS,MAAM,QAAQ,KAAK;AAAA,MAC5B,WAAW,MAAM;AAAA,MACjB,UAAU,MAAM;AAAA,MAChB,SAAS,MAAM;AAAA,MACf,SAAS,MAAM,SAAS,KAAK;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEQ,wBAA8B;AAEpC,eAAW,YAAY,KAAK,OAAO,sBAAsB;AACvD,WAAK,oBAAoB,QAAQ;AAAA,IACnC;AAAA,EACF;AAAA,EAEQ,oBAAoB,UAAwB;AAElD,UAAM,aAAa,KAAK,oBAAoB,QAAQ;AAEpD,gBAAY,YAAY;AACtB,UAAI;AACF,cAAM,KAAK,eAAe,QAAQ;AAAA,MACpC,SAAS,OAAO;AACd,gBAAQ,MAAM,mCAAmC,QAAQ,KAAK,KAAK;AAAA,MACrE;AAAA,IACF,GAAG,UAAU;AAAA,EACf;AAAA,EAEQ,oBAAoB,UAA0B;AACpD,YAAQ,UAAU;AAAA,MAChB,KAAK;AAAU,eAAO,KAAK;AAAA,MAC3B,KAAK;AAAQ,eAAO,KAAK,KAAK;AAAA,MAC9B,KAAK;AAAO,eAAO,KAAK,KAAK,KAAK;AAAA,MAClC,KAAK;AAAQ,eAAO,IAAI,KAAK,KAAK,KAAK;AAAA,MACvC,KAAK;AAAS,eAAO,KAAK,KAAK,KAAK,KAAK;AAAA,MACzC;AAAS,eAAO,KAAK,KAAK;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,UAAiC;AAE5D,YAAQ,IAAI,WAAW,QAAQ,iBAAiB;AAAA,EAClD;AAAA,EAEA,MAAc,gBAAgB,WAAyC;AAErE,eAAW,WAAW,WAAW;AAC/B,UAAI,QAAQ,aAAa,cAAc,QAAQ,aAAa,QAAQ;AAClE,gBAAQ,KAAK,qBAAqB,OAAO;AAAA,MAE3C;AAAA,IACF;AAAA,EACF;AACF;;;ACzQO,IAAM,uBAAN,MAA2B;AAAA,EACxB;AAAA,EAER,YAAY,WAAW,OAAO;AAC5B,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UACJ,SACA,UACA,UAA8B,EAAE,UAAU,OAAO,WAAW,EAAE,GACjC;AAC7B,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,gBAAgB,KAAK,YAAY,OAAO;AAC9C,UAAM,aAAiC,CAAC;AAExC,eAAW,CAAC,MAAM,WAAW,KAAK,cAAc,QAAQ,GAAG;AAEzD,YAAM,sBAAsB,KAAK,kBAAkB,WAAW;AAE9D,iBAAW,CAAC,cAAc,gBAAgB,KAAK,oBAAoB,QAAQ,GAAG;AAC5E,cAAM,iBAAiB,MAAM,KAAK;AAAA,UAChC;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK,MAAM,YAAY;AAAA,UACvB;AAAA,QACF;AACA,mBAAW,KAAK,GAAG,cAAc;AAAA,MACnC;AAAA,IACF;AAEA,WAAO,WAAW,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAAA,EAChF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,SACA,YACA,OAAe,YACc;AAC7B,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,gBAAgB,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAC/F,UAAM,YAAY,cAAc,CAAC,EAAE;AACnC,UAAM,UAAU,cAAc,cAAc,SAAS,CAAC,EAAE;AAExD,UAAM,aAAiC,CAAC;AACxC,UAAM,WAAW,aAAa,KAAK;AACnC,UAAM,SAAS,OAAO,KAAK;AAE3B,QAAI,cAAc,UAAU,QAAQ;AACpC,UAAM,YAAY,QAAQ,QAAQ;AAElC,WAAO,eAAe,WAAW;AAC/B,YAAM,cAAc,IAAI,KAAK,WAAW;AACxC,YAAM,YAAY,IAAI,KAAK,cAAc,QAAQ;AAEjD,YAAM,gBAAgB,cAAc;AAAA,QAAO,OACzC,EAAE,aAAa,eAAe,EAAE,YAAY;AAAA,MAC9C;AAEA,UAAI,cAAc,SAAS,GAAG;AAC5B,cAAM,mBAAmB,MAAM,KAAK,gBAAgB,eAAe,WAAW;AAC9E,mBAAW,KAAK,GAAG,gBAAgB;AAAA,MACrC;AAEA,qBAAe;AAAA,IACjB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBACJ,SACA,eAAuB,IAKtB;AACD,QAAI,QAAQ,SAAS,eAAe,GAAG;AACrC,aAAO,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,GAAG,UAAU,CAAC,EAAE;AAAA,IACjD;AAEA,UAAM,gBAAgB,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAG/F,UAAM,QAAQ,KAAK,uBAAuB,eAAe,YAAY;AAGrE,UAAM,WAAW,KAAK,2BAA2B,eAAe,OAAO,YAAY;AAGnF,UAAM,WAAW,KAAK,kBAAkB,eAAe,OAAO,QAAQ;AAEtE,WAAO,EAAE,OAAO,UAAU,SAAS;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,SACA,gBAC6B;AAC7B,UAAM,gBAAgB,KAAK,oBAAoB,SAAS,cAAc;AACtE,UAAM,cAAkC,CAAC;AAEzC,eAAW,CAAC,SAAS,WAAW,KAAK,cAAc,QAAQ,GAAG;AAC5D,YAAM,YAAY,IAAI,KAAK,OAAO;AAGlC,YAAM,UAAU,oBAAI,IAA6C;AAEjE,iBAAW,UAAU,aAAa;AAChC,cAAM,UAAU,OAAO,KAAK,SAAS;AACrC,cAAM,eAAe,KAAK,UAAU,OAAO,UAAU;AAErD,YAAI,CAAC,QAAQ,IAAI,OAAO,GAAG;AACzB,kBAAQ,IAAI,SAAS,oBAAI,IAAI,CAAC;AAAA,QAChC;AACA,YAAI,CAAC,QAAQ,IAAI,OAAO,EAAG,IAAI,YAAY,GAAG;AAC5C,kBAAQ,IAAI,OAAO,EAAG,IAAI,cAAc,CAAC,CAAC;AAAA,QAC5C;AAEA,gBAAQ,IAAI,OAAO,EAAG,IAAI,YAAY,EAAG,KAAK,MAAM;AAAA,MACtD;AAGA,iBAAW,CAAC,SAAS,UAAU,KAAK,QAAQ,QAAQ,GAAG;AACrD,mBAAW,CAAC,cAAc,gBAAgB,KAAK,WAAW,QAAQ,GAAG;AACnE,gBAAM,eAAe,KAAK,sBAAsB,iBAAiB,IAAI,OAAK,EAAE,aAAa,GAAG,CAAC;AAE7F,sBAAY,KAAK;AAAA,YACf,MAAM,iBAAiB,CAAC,EAAE;AAAA,YAC1B,UAAU;AAAA,YACV;AAAA,YACA,YAAY,KAAK,MAAM,YAAY;AAAA,YACnC;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO,YAAY,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAAA,EACjF;AAAA,EAEQ,YAAY,SAAsD;AACxE,UAAM,UAAU,oBAAI,IAA8B;AAElD,eAAW,UAAU,SAAS;AAC5B,UAAI,CAAC,QAAQ,IAAI,OAAO,IAAI,GAAG;AAC7B,gBAAQ,IAAI,OAAO,MAAM,CAAC,CAAC;AAAA,MAC7B;AACA,cAAQ,IAAI,OAAO,IAAI,EAAG,KAAK,MAAM;AAAA,IACvC;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,SAAkD;AAC1E,UAAM,UAAU,oBAAI,IAA0B;AAE9C,eAAW,UAAU,SAAS;AAC5B,YAAM,MAAM,KAAK,UAAU,OAAO,UAAU;AAC5C,UAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,gBAAQ,IAAI,KAAK,CAAC,CAAC;AAAA,MACrB;AACA,cAAQ,IAAI,GAAG,EAAG,KAAK,MAAM;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,oBACZ,SACA,MACA,UACA,YACA,SAC6B;AAC7B,UAAM,UAAU,KAAK,oBAAoB,SAAS,QAAQ;AAC1D,UAAM,aAAiC,CAAC;AAGxC,QAAI,QAAQ,YAAY,QAAQ,OAAO,GAAG;AACxC,YAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,EAAE,QAAQ,IAAI,IAAI,KAAK,CAAC,EAAE,QAAQ,CAAC;AACxG,YAAM,eAAe,KAAK,aAAa,UAAU,UAAU,QAAQ,SAAS;AAE5E,iBAAW,CAAC,SAAS,YAAY,KAAK,aAAa,QAAQ,GAAG;AAC5D,YAAI,CAAC,QAAQ,IAAI,OAAO,GAAG;AACzB,kBAAQ,IAAI,SAAS,CAAC;AAAA,YACpB,IAAI,UAAU,KAAK,IAAI,CAAC;AAAA,YACxB;AAAA,YACA,WAAW,IAAI,KAAK,OAAO;AAAA,YAC3B,OAAO;AAAA,YACP;AAAA,UACF,CAAC,CAAC;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAEA,eAAW,CAAC,SAAS,WAAW,KAAK,QAAQ,QAAQ,GAAG;AACtD,YAAM,YAAY,IAAI,KAAK,OAAO;AAClC,YAAM,SAAS,YAAY,IAAI,OAAK,EAAE,KAAK;AAC3C,YAAM,eAAe,KAAK,sBAAsB,MAAM;AAEtD,iBAAW,KAAK;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,oBACN,SACA,UACoB;AACpB,UAAM,UAAU,oBAAI,IAAmB;AAEvC,eAAW,UAAU,SAAS;AAC5B,YAAM,UAAU,KAAK,mBAAmB,OAAO,WAAW,QAAQ;AAClE,UAAI,CAAC,QAAQ,IAAI,OAAO,GAAG;AACzB,gBAAQ,IAAI,SAAS,CAAC,CAAC;AAAA,MACzB;AACA,cAAQ,IAAI,OAAO,EAAG,KAAK,MAAM;AAAA,IACnC;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAmB,WAAiB,UAAgE;AAC1G,UAAM,OAAO,IAAI,KAAK,SAAS;AAE/B,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,aAAK,WAAW,GAAG,CAAC;AACpB;AAAA,MACF,KAAK;AACH,aAAK,WAAW,GAAG,GAAG,CAAC;AACvB;AAAA,MACF,KAAK;AACH,aAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AACxB;AAAA,MACF,KAAK;AACH,cAAM,YAAY,KAAK,OAAO;AAC9B,cAAM,OAAO,KAAK,QAAQ,IAAI;AAC9B,aAAK,QAAQ,IAAI;AACjB,aAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AACxB;AAAA,MACF,KAAK;AACH,aAAK,QAAQ,CAAC;AACd,aAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AACxB;AAAA,IACJ;AAEA,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA,EAEQ,aACN,UACA,UACA,WACqB;AACrB,UAAM,SAAS,oBAAI,IAAoB;AAEvC,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,IAAI,KAAK,SAAS,CAAC,CAAC;AAClC,UAAM,MAAM,IAAI,KAAK,SAAS,SAAS,SAAS,CAAC,CAAC;AAElD,QAAI,UAAU,IAAI,KAAK,KAAK;AAE5B,WAAO,WAAW,KAAK;AACrB,YAAM,MAAM,QAAQ,YAAY;AAChC,aAAO,IAAI,KAAK,SAAS;AAGzB,cAAQ,UAAU;AAAA,QAChB,KAAK;AACH,kBAAQ,WAAW,QAAQ,WAAW,IAAI,CAAC;AAC3C;AAAA,QACF,KAAK;AACH,kBAAQ,SAAS,QAAQ,SAAS,IAAI,CAAC;AACvC;AAAA,QACF,KAAK;AACH,kBAAQ,QAAQ,QAAQ,QAAQ,IAAI,CAAC;AACrC;AAAA,QACF,KAAK;AACH,kBAAQ,QAAQ,QAAQ,QAAQ,IAAI,CAAC;AACrC;AAAA,QACF,KAAK;AACH,kBAAQ,SAAS,QAAQ,SAAS,IAAI,CAAC;AACvC;AAAA,MACJ;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,sBAAsB,QAAkB;AAC9C,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO,EAAE,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE;AAAA,IACpD;AAEA,UAAM,MAAM,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,KAAK,CAAC;AACpD,UAAM,QAAQ,OAAO;AACrB,UAAM,MAAM,MAAM;AAClB,UAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,UAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAE9B,WAAO,EAAE,OAAO,KAAK,KAAK,KAAK,IAAI;AAAA,EACrC;AAAA,EAEA,MAAc,gBAAgB,SAAuB,aAAgD;AACnG,UAAM,UAAU,KAAK,YAAY,OAAO;AACxC,UAAM,aAAiC,CAAC;AAExC,eAAW,CAAC,MAAM,WAAW,KAAK,QAAQ,QAAQ,GAAG;AACnD,YAAM,kBAAkB,KAAK,kBAAkB,WAAW;AAE1D,iBAAW,CAAC,cAAc,gBAAgB,KAAK,gBAAgB,QAAQ,GAAG;AACxE,cAAM,SAAS,iBAAiB,IAAI,OAAK,EAAE,KAAK;AAChD,cAAM,eAAe,KAAK,sBAAsB,MAAM;AAEtD,mBAAW,KAAK;AAAA,UACd;AAAA,UACA,UAAU;AAAA;AAAA,UACV,WAAW;AAAA,UACX,YAAY,KAAK,MAAM,YAAY;AAAA,UACnC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,SAA6B,YAAwC;AAClG,UAAM,QAA4B,CAAC;AAEnC,aAAS,IAAI,KAAK,MAAM,aAAa,CAAC,GAAG,IAAI,QAAQ,SAAS,KAAK,MAAM,aAAa,CAAC,GAAG,KAAK;AAC7F,YAAM,SAAS,QAAQ,MAAM,IAAI,KAAK,MAAM,aAAa,CAAC,GAAG,IAAI,KAAK,MAAM,aAAa,CAAC,IAAI,CAAC;AAC/F,YAAM,WAAW,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC,IAAI,OAAO;AAEjF,YAAM,KAAK;AAAA,QACT,GAAG,QAAQ,CAAC;AAAA,QACZ,cAAc;AAAA,UACZ,GAAG,QAAQ,CAAC,EAAE;AAAA,UACd,KAAK;AAAA,UACL,KAAK,WAAW,QAAQ,CAAC,EAAE,aAAa;AAAA,QAC1C;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,2BACN,SACA,OACA,cACoB;AACpB,UAAM,WAA+B,CAAC;AACtC,UAAM,kBAAkB,IAAI,MAAM,YAAY,EAAE,KAAK,CAAC;AACtD,UAAM,iBAAiB,IAAI,MAAM,YAAY,EAAE,KAAK,CAAC;AAGrD,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,cAAc,IAAI;AACxB,YAAM,aAAa,MAAM,KAAK,OAAK,EAAE,UAAU,QAAQ,MAAM,QAAQ,CAAC,EAAE,UAAU,QAAQ,CAAC;AAE3F,UAAI,YAAY;AACd,wBAAgB,WAAW,KAAK,QAAQ,CAAC,EAAE,aAAa,MAAM,WAAW,aAAa;AACtF,uBAAe,WAAW;AAAA,MAC5B;AAAA,IACF;AAGA,aAAS,IAAI,GAAG,IAAI,cAAc,KAAK;AACrC,UAAI,eAAe,CAAC,IAAI,GAAG;AACzB,wBAAgB,CAAC,KAAK,eAAe,CAAC;AAAA,MACxC;AAAA,IACF;AAGA,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,cAAc,IAAI;AACxB,eAAS,KAAK;AAAA,QACZ,GAAG,QAAQ,CAAC;AAAA,QACZ,cAAc;AAAA,UACZ,GAAG,QAAQ,CAAC,EAAE;AAAA,UACd,KAAK,gBAAgB,WAAW;AAAA,UAChC,KAAK,gBAAgB,WAAW,IAAI,QAAQ,CAAC,EAAE,aAAa;AAAA,QAC9D;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBACN,UACA,OACA,UACoB;AACpB,UAAM,WAA+B,CAAC;AAEtC,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,aAAa,MAAM,KAAK,OAAK,EAAE,UAAU,QAAQ,MAAM,SAAS,CAAC,EAAE,UAAU,QAAQ,CAAC;AAC5F,YAAM,gBAAgB,SAAS,CAAC;AAEhC,UAAI,gBAAgB,SAAS,CAAC,EAAE,aAAa;AAC7C,UAAI,YAAY;AACd,yBAAiB,WAAW,aAAa;AAAA,MAC3C;AACA,UAAI,eAAe;AACjB,yBAAiB,cAAc,aAAa;AAAA,MAC9C;AAEA,eAAS,KAAK;AAAA,QACZ,GAAG,SAAS,CAAC;AAAA,QACb,cAAc;AAAA,UACZ,GAAG,SAAS,CAAC,EAAE;AAAA,UACf,KAAK;AAAA,UACL,KAAK,gBAAgB,SAAS,CAAC,EAAE,aAAa;AAAA,QAChD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;;;AC/bO,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA,SAAoC,oBAAI,IAAI;AAAA,EAC5C,mBAAoD,oBAAI,IAAI;AAAA,EAEpE,YAAY,QAA0B;AACpC,SAAK,SAAS;AACd,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAU,QAAmC;AACjD,UAAM,YAAY,KAAK,aAAa,MAAM;AAE1C,QAAI,CAAC,KAAK,OAAO,IAAI,SAAS,GAAG;AAC/B,WAAK,OAAO,IAAI,WAAW,CAAC,CAAC;AAAA,IAC/B;AAEA,SAAK,OAAO,IAAI,SAAS,EAAG,KAAK,MAAM;AAGvC,QAAI,KAAK,OAAO,IAAI,SAAS,EAAG,UAAU,KAAK,OAAO,WAAW;AAC/D,YAAM,KAAK,YAAY,SAAS;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,SAAsC;AACrD,eAAW,UAAU,SAAS;AAC5B,YAAM,KAAK,UAAU,MAAM;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB,SAAoD;AACzE,UAAM,UAA8B,CAAC;AAErC,eAAW,QAAQ,KAAK,OAAO,OAAO;AACpC,YAAM,oBAAoB,KAAK,oBAAoB,SAAS,IAAI;AAChE,UAAI,kBAAkB,WAAW,EAAG;AAEpC,YAAM,aAAa,MAAM,KAAK,qBAAqB,mBAAmB,IAAI;AAC1E,cAAQ,KAAK,GAAG,UAAU;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,SACA,SACA,iBACA,SAC6B;AAC7B,QAAI,kBAAkB;AAGtB,QAAI,SAAS;AACX,wBAAkB,KAAK,aAAa,SAAS,OAAO;AAAA,IACtD;AAGA,UAAM,UAAU,KAAK,aAAa,iBAAiB,OAAO;AAC1D,UAAM,UAA8B,CAAC;AAErC,eAAW,CAAC,UAAU,YAAY,KAAK,QAAQ,QAAQ,GAAG;AACxD,YAAM,aAAa,KAAK,MAAM,QAAQ;AACtC,YAAM,aAAa,MAAM,KAAK,mBAAmB,cAAc,iBAAiB,UAAU;AAC1F,cAAQ,KAAK,GAAG,UAAU;AAAA,IAC5B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eACJ,kBACA,oBACA,UAAoB,CAAC,GACQ;AAC7B,UAAM,aAAa,KAAK,aAAa,kBAAkB,OAAO;AAC9D,UAAM,aAAa,KAAK,aAAa,oBAAoB,OAAO;AAChE,UAAM,QAA4B,CAAC;AAEnC,eAAW,CAAC,UAAU,UAAU,KAAK,WAAW,QAAQ,GAAG;AACzD,YAAM,aAAa,WAAW,IAAI,QAAQ,KAAK,CAAC;AAChD,YAAM,aAAa,KAAK,MAAM,QAAQ;AAEtC,YAAM,SAAS,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAC7D,YAAM,SAAS,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAC7D,YAAM,OAAO,SAAS,IAAK,SAAS,SAAU,MAAM;AAGpD,YAAM,kBAAkB,IAAI,KAAK,KAAK;AAAA,QACpC,GAAG,WAAW,IAAI,OAAK,EAAE,UAAU,QAAQ,CAAC;AAAA,QAC5C,GAAG,WAAW,IAAI,OAAK,EAAE,UAAU,QAAQ,CAAC;AAAA,MAC9C,CAAC;AAED,YAAM,KAAK;AAAA,QACT,MAAM,WAAW,CAAC,GAAG;AAAA,QACrB,UAAU;AAAA,QACV,WAAW;AAAA,QACX;AAAA,QACA,cAAc;AAAA,UACZ,OAAO,WAAW,SAAS,WAAW;AAAA,UACtC,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBACJ,SACA,aACA,UAAoB,CAAC,GACQ;AAC7B,UAAM,UAAU,KAAK,aAAa,SAAS,OAAO;AAClD,UAAM,UAA8B,CAAC;AAErC,eAAW,CAAC,UAAU,YAAY,KAAK,QAAQ,QAAQ,GAAG;AACxD,YAAM,aAAa,KAAK,MAAM,QAAQ;AACtC,YAAM,SAAS,aAAa,IAAI,OAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAElE,iBAAW,cAAc,aAAa;AACpC,cAAM,QAAQ,KAAK,oBAAoB,QAAQ,UAAU;AAEzD,gBAAQ,KAAK;AAAA,UACX,MAAM,aAAa,CAAC,EAAE;AAAA,UACtB,UAAU;AAAA,UACV,WAAW,oBAAI,KAAK;AAAA,UACpB,YAAY,EAAE,GAAG,YAAY,YAAY,WAAW,SAAS,EAAE;AAAA,UAC/D,cAAc;AAAA,YACZ,OAAO,OAAO;AAAA,YACd,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,YACL,KAAK;AAAA,UACP;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,uBACJ,SACA,cACA,QACA,iBAC6B;AAC7B,UAAM,gBAAgB,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAC/F,QAAI,cAAc,WAAW,EAAG,QAAO,CAAC;AAExC,UAAM,UAA8B,CAAC;AACrC,UAAM,YAAY,cAAc,CAAC,EAAE,UAAU,QAAQ;AACrD,UAAM,UAAU,cAAc,cAAc,SAAS,CAAC,EAAE,UAAU,QAAQ;AAE1E,QAAI,cAAc;AAClB,WAAO,eAAe,SAAS;AAC7B,YAAM,cAAc,IAAI,KAAK,WAAW;AACxC,YAAM,YAAY,IAAI,KAAK,cAAc,YAAY;AAErD,YAAM,gBAAgB,cAAc;AAAA,QAAO,OACzC,EAAE,aAAa,eAAe,EAAE,YAAY;AAAA,MAC9C;AAEA,UAAI,cAAc,SAAS,GAAG;AAC5B,cAAM,aAAa,MAAM,KAAK,mBAAmB,eAAe,iBAAiB,CAAC,CAAC;AACnF,gBAAQ,KAAK,GAAG,UAAU;AAAA,MAC5B;AAEA,qBAAe;AAAA,IACjB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,SACA,QAC6B;AAC7B,UAAM,SAAS,oBAAI,IAAoC;AAGvD,eAAW,UAAU,SAAS;AAC5B,UAAI,CAAC,OAAO,IAAI,OAAO,IAAI,GAAG;AAC5B,eAAO,IAAI,OAAO,MAAM,CAAC,CAAC;AAAA,MAC5B;AACA,aAAO,IAAI,OAAO,IAAI,EAAG,KAAK,MAAM;AAAA,IACtC;AAEA,UAAM,aAAiC,CAAC;AAExC,eAAW,CAAC,MAAM,WAAW,KAAK,OAAO,QAAQ,GAAG;AAClD,YAAM,SAAS,YAAY,IAAI,OAAK,EAAE,aAAa,GAAG;AACtD,UAAI;AAEJ,cAAQ,QAAQ;AAAA,QACd,KAAK;AACH,6BAAmB,KAAK,gBAAgB,MAAM;AAC9C;AAAA,QACF,KAAK;AACH,6BAAmB,KAAK,gBAAgB,MAAM;AAC9C;AAAA,QACF,KAAK;AACH,6BAAmB,KAAK,gBAAgB,MAAM;AAC9C;AAAA,QACF;AACE,6BAAmB;AAAA,MACvB;AAEA,eAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,mBAAW,KAAK;AAAA,UACd,GAAG,YAAY,CAAC;AAAA,UAChB,cAAc;AAAA,YACZ,GAAG,YAAY,CAAC,EAAE;AAAA,YAClB,KAAK,iBAAiB,CAAC;AAAA,YACvB,KAAK,iBAAiB,CAAC,IAAI,YAAY,CAAC,EAAE,aAAa;AAAA,UACzD;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,QAA4B;AAC/C,WAAO,GAAG,OAAO,IAAI,IAAI,KAAK,UAAU,OAAO,UAAU,CAAC;AAAA,EAC5D;AAAA,EAEA,MAAc,YAAY,WAAkC;AAC1D,UAAM,UAAU,KAAK,OAAO,IAAI,SAAS;AACzC,QAAI,CAAC,WAAW,QAAQ,WAAW,EAAG;AAGtC,UAAM,aAAa,MAAM,KAAK,iBAAiB,OAAO;AAGtD,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,WAAW,GAAG,SAAS,IAAI,KAAK,IAAI,CAAC;AAC3C,WAAK,iBAAiB,IAAI,UAAU,UAAU;AAAA,IAChD;AAGA,SAAK,OAAO,IAAI,WAAW,CAAC,CAAC;AAAA,EAC/B;AAAA,EAEQ,oBAAoB,SAAuB,MAAqC;AACtF,QAAI,WAAW,QAAQ,OAAO,OAAK,EAAE,SAAS,KAAK,UAAU;AAE7D,QAAI,KAAK,YAAY;AACnB,iBAAW,SAAS,OAAO,YAAU;AACnC,eAAO,KAAK,WAAY,MAAM,eAAa;AACzC,gBAAM,QAAQ,KAAK,cAAc,QAAQ,UAAU,KAAK;AACxD,iBAAO,KAAK,kBAAkB,OAAO,UAAU,UAAU,UAAU,KAAK;AAAA,QAC1E,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,qBACZ,SACA,MAC6B;AAC7B,UAAM,UAAU,KAAK,aAAa,SAAS,KAAK,UAAU;AAC1D,UAAM,UAA8B,CAAC;AAErC,eAAW,CAAC,UAAU,YAAY,KAAK,QAAQ,QAAQ,GAAG;AACxD,YAAM,aAAa,KAAK,MAAM,QAAQ;AAEtC,UAAI,KAAK,oBAAoB,cAAc;AACzC,YAAI,KAAK,YAAY;AACnB,gBAAM,SAAS,aAAa,IAAI,OAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAClE,gBAAM,kBAAkB,KAAK,oBAAoB,QAAQ,KAAK,UAAU;AAExE,kBAAQ,KAAK;AAAA,YACX,MAAM,KAAK;AAAA,YACX,UAAU;AAAA,YACV,WAAW,oBAAI,KAAK;AAAA,YACpB;AAAA,YACA,cAAc;AAAA,cACZ,OAAO,OAAO;AAAA,cACd,KAAK;AAAA,cACL,KAAK;AAAA,cACL,KAAK;AAAA,cACL,KAAK;AAAA,YACP;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MAEF,OAAO;AACL,cAAM,aAAa,MAAM,KAAK,mBAAmB,cAAc,KAAK,iBAAiB,UAAU;AAC/F,gBAAQ,KAAK,GAAG,UAAU;AAAA,MAC5B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,SAAuB,SAA8C;AACxF,UAAM,UAAU,oBAAI,IAA0B;AAE9C,eAAW,UAAU,SAAS;AAC5B,YAAM,WAAW,KAAK,eAAe,QAAQ,OAAO;AAEpD,UAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG;AAC1B,gBAAQ,IAAI,UAAU,CAAC,CAAC;AAAA,MAC1B;AACA,cAAQ,IAAI,QAAQ,EAAG,KAAK,MAAM;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,QAAoB,SAA2B;AACpE,UAAM,kBAA0C,CAAC;AAEjD,eAAW,SAAS,SAAS;AAC3B,sBAAgB,KAAK,IAAI,KAAK,cAAc,QAAQ,KAAK;AAAA,IAC3D;AAEA,WAAO,KAAK,UAAU,eAAe;AAAA,EACvC;AAAA,EAEQ,cAAc,QAAoB,OAAuB;AAC/D,QAAI,UAAU,OAAQ,QAAO,OAAO,KAAK,SAAS;AAClD,QAAI,UAAU,YAAa,QAAO,OAAO,UAAU,YAAY;AAC/D,WAAO,OAAO,WAAW,KAAK,KAAK;AAAA,EACrC;AAAA,EAEQ,kBAAkB,OAAY,UAAkB,UAAwB;AAC9E,YAAQ,UAAU;AAAA,MAChB,KAAK;AAAU,eAAO,UAAU;AAAA,MAChC,KAAK;AAAc,eAAO,UAAU;AAAA,MACpC,KAAK;AAAM,eAAO,OAAO,KAAK,IAAI,OAAO,QAAQ;AAAA,MACjD,KAAK;AAAM,eAAO,OAAO,KAAK,IAAI,OAAO,QAAQ;AAAA,MACjD,KAAK;AAAY,eAAO,OAAO,KAAK,EAAE,SAAS,OAAO,QAAQ,CAAC;AAAA,MAC/D;AAAS,eAAO;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,SACA,iBACA,YAC6B;AAC7B,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,UAAM,SAAS,QAAQ,IAAI,OAAK,EAAE,KAAK;AACvC,QAAI;AAEJ,YAAQ,iBAAiB;AAAA,MACvB,KAAK;AACH,0BAAkB,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,KAAK,CAAC;AAC1D;AAAA,MACF,KAAK;AACH,0BAAkB,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,KAAK,CAAC,IAAI,OAAO;AACrE;AAAA,MACF,KAAK;AACH,0BAAkB,KAAK,IAAI,GAAG,MAAM;AACpC;AAAA,MACF,KAAK;AACH,0BAAkB,KAAK,IAAI,GAAG,MAAM;AACpC;AAAA,MACF,KAAK;AACH,0BAAkB,OAAO;AACzB;AAAA,MACF,KAAK;AAEH,0BAAkB;AAClB;AAAA,MACF;AACE,0BAAkB;AAAA,IACtB;AAEA,WAAO,CAAC;AAAA,MACN,MAAM,QAAQ,CAAC,EAAE;AAAA,MACjB,UAAU;AAAA,MACV,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA,cAAc;AAAA,QACZ,OAAO,OAAO;AAAA,QACd,KAAK,oBAAoB,QAAQ,kBAAkB,OAAO,OAAO,CAAC,KAAK,QAAQ,MAAM,KAAK,CAAC;AAAA,QAC3F,KAAK,oBAAoB,QAAQ,kBAAkB;AAAA,QACnD,KAAK,KAAK,IAAI,GAAG,MAAM;AAAA,QACvB,KAAK,KAAK,IAAI,GAAG,MAAM;AAAA,MACzB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,aAAa,SAAuB,SAA4C;AACtF,WAAO,QAAQ,OAAO,YAAU;AAC9B,aAAO,OAAO,QAAQ,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,KAAK,MAAM;AACrD,cAAM,cAAc,KAAK,cAAc,QAAQ,GAAG;AAClD,eAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS,WAAW,IAAI,gBAAgB;AAAA,MAC9E,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAoB,QAAkB,YAA4B;AACxE,QAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,UAAM,QAAS,aAAa,OAAQ,OAAO,SAAS;AACpD,UAAM,QAAQ,KAAK,MAAM,KAAK;AAC9B,UAAM,QAAQ,KAAK,KAAK,KAAK;AAE7B,QAAI,UAAU,OAAO;AACnB,aAAO,OAAO,KAAK;AAAA,IACrB;AAEA,UAAM,SAAS,QAAQ;AACvB,WAAO,OAAO,KAAK,KAAK,IAAI,UAAU,OAAO,KAAK,IAAI;AAAA,EACxD;AAAA,EAEQ,gBAAgB,QAA4B;AAClD,UAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,UAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,UAAM,QAAQ,MAAM;AAEpB,WAAO,UAAU,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,IAAI,QAAM,IAAI,OAAO,KAAK;AAAA,EAC9E;AAAA,EAEQ,gBAAgB,QAA4B;AAClD,UAAM,OAAO,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,OAAO;AAC5D,UAAM,SAAS,KAAK,KAAK,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,MAAM;AAElG,WAAO,WAAW,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,IAAI,QAAM,IAAI,QAAQ,MAAM;AAAA,EACjF;AAAA,EAEQ,gBAAgB,QAA4B;AAClD,UAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/C,UAAM,SAAS,KAAK,oBAAoB,QAAQ,EAAE;AAClD,UAAM,MAAM,KAAK,aAAa,QAAQ,MAAM;AAE5C,WAAO,QAAQ,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,IAAI,QAAM,IAAI,UAAU,GAAG;AAAA,EAC7E;AAAA,EAEQ,aAAa,QAAkB,QAAwB;AAC7D,UAAM,gBAAgB,OAAO,IAAI,OAAK,KAAK,IAAI,IAAI,MAAM,CAAC;AAC1D,WAAO,KAAK,oBAAoB,cAAc,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;AAAA,EACzE;AAAA,EAEQ,qBAA2B;AACjC,gBAAY,YAAY;AACtB,iBAAW,aAAa,KAAK,OAAO,KAAK,GAAG;AAC1C,cAAM,KAAK,YAAY,SAAS;AAAA,MAClC;AAAA,IACF,GAAG,KAAK,OAAO,aAAa;AAAA,EAC9B;AACF;;;AClfA,oBAA6B;AAiCtB,IAAM,iBAAN,cAA6B,2BAAa;AAAA,EACvC;AAAA,EACA,SAAsB,CAAC;AAAA,EACvB,aAA0C,oBAAI,IAAI;AAAA,EAClD,eAAoC,oBAAI,IAAI;AAAA;AAAA,EAC5C,UAAwB,CAAC;AAAA,EAEzB,gBAAsC;AAAA,IAC5C,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,qBAAqB;AAAA,IACrB,qBAAqB;AAAA;AAAA,IACrB,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EAEA,YAAY,SAAwC,CAAC,GAAG;AACtD,UAAM;AACN,SAAK,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,OAAO;AACjD,SAAK,4BAA4B;AACjC,SAAK,mBAAmB;AACxB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,OAAiC;AAElD,QAAI,KAAK,OAAO,kBAAkB,KAAK,OAAO,IAAI,KAAK,OAAO,cAAc;AAC1E;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,uBAAuB,KAAK,YAAY,KAAK,GAAG;AAC9D;AAAA,IACF;AAGA,SAAK,cAAc,KAAK;AAGxB,SAAK,OAAO,KAAK,KAAK;AAGtB,QAAI,KAAK,oBAAoB,KAAK,GAAG;AACnC,YAAM,KAAK,aAAa,KAAK;AAAA,IAC/B;AAGA,QAAI,KAAK,OAAO,UAAU,KAAK,OAAO,YAAY;AAChD,YAAM,KAAK,MAAM;AAAA,IACnB;AAEA,SAAK,KAAK,mBAAmB,KAAK;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,QAAoC;AACtD,eAAW,SAAS,QAAQ;AAC1B,YAAM,KAAK,aAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,MAAc,WAAiC;AAC/D,SAAK,WAAW,IAAI,MAAM,SAAS;AACnC,SAAK,KAAK,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,MAAuB;AACzC,UAAM,UAAU,KAAK,WAAW,OAAO,IAAI;AAC3C,QAAI,SAAS;AACX,WAAK,KAAK,0BAA0B,EAAE,KAAK,CAAC;AAAA,IAC9C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,OAA4B;AAC9C,QAAI,CAAC,OAAO;AACV,aAAO,CAAC,GAAG,KAAK,OAAO;AAAA,IACzB;AAEA,WAAO,KAAK,QAAQ,OAAO,OAAK,EAAE,aAAa,KAAK;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,gBAA8C;AACnD,WAAO,MAAM;AACX,YAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,KAAK,OAAO,aAAa,CAAC;AAE3E,UAAI,KAAK,OAAO,SAAS,GAAG;AAC1B,cAAM,KAAK,MAAM;AAAA,MACnB;AAEA,YAAM,gBAAgB,KAAK;AAAA,QACzB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO,aAAa;AAAA,MACjD;AAEA,UAAI,cAAc,SAAS,GAAG;AAC5B,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAME;AACA,UAAM,eAAuC,CAAC;AAC9C,UAAM,iBAAyC,CAAC;AAGhD,eAAW,SAAS,KAAK,QAAQ;AAC/B,mBAAa,MAAM,IAAI,KAAK,aAAa,MAAM,IAAI,KAAK,KAAK;AAC7D,qBAAe,MAAM,MAAM,KAAK,eAAe,MAAM,MAAM,KAAK,KAAK;AAAA,IACvE;AAEA,WAAO;AAAA,MACL,aAAa,KAAK,OAAO;AAAA,MACzB;AAAA,MACA;AAAA,MACA,YAAY,KAAK,OAAO;AAAA,MACxB,kBAAkB,KAAK,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,OAAO,WAAW,GAAG;AAC5B;AAAA,IACF;AAEA,UAAM,SAAS,CAAC,GAAG,KAAK,MAAM;AAC9B,SAAK,SAAS,CAAC;AAEf,QAAI;AACF,YAAM,aAA2B,CAAC;AAElC,iBAAW,SAAS,QAAQ;AAC1B,cAAM,UAAU,MAAM,KAAK,aAAa,KAAK;AAC7C,mBAAW,KAAK,GAAG,OAAO;AAAA,MAC5B;AAEA,WAAK,QAAQ,KAAK,GAAG,UAAU;AAG/B,UAAI,KAAK,QAAQ,SAAS,KAAQ;AAChC,aAAK,UAAU,KAAK,QAAQ,MAAM,IAAM;AAAA,MAC1C;AAEA,WAAK,KAAK,kBAAkB;AAAA,QAC1B,YAAY,OAAO;AAAA,QACnB,aAAa,WAAW;AAAA,MAC1B,CAAC;AAAA,IAEH,SAAS,OAAO;AAEd,WAAK,OAAO,QAAQ,GAAG,MAAM;AAC7B,WAAK,KAAK,eAAe,KAAK;AAC9B,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,aAAa,OAAyC;AAClE,UAAM,UAAwB,CAAC;AAE/B,eAAW,CAAC,MAAM,SAAS,KAAK,KAAK,WAAW,QAAQ,GAAG;AACzD,UAAI;AACF,YAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,gBAAM,mBAAmB,MAAM,UAAU,QAAQ,KAAK;AACtD,kBAAQ,KAAK,GAAG,gBAAgB;AAAA,QAClC;AAAA,MACF,SAAS,OAAO;AACd,aAAK,KAAK,mBAAmB,EAAE,eAAe,MAAM,OAAO,MAAM,CAAC;AAAA,MACpE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,OAAwB;AAC5C,QAAI,CAAC,MAAM,IAAI;AACb,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,QAAI,CAAC,MAAM,aAAa,EAAE,MAAM,qBAAqB,OAAO;AAC1D,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAEA,QAAI,CAAC,MAAM,QAAQ;AACjB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,QAAI,CAAC,MAAM,WAAW,OAAO,MAAM,YAAY,UAAU;AACvD,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAAA,EACF;AAAA,EAEQ,YAAY,OAA2B;AAC7C,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WAAW,GAAG,MAAM,EAAE,IAAI,MAAM,IAAI,IAAI,MAAM,MAAM;AAC1D,UAAM,WAAW,KAAK,aAAa,IAAI,QAAQ;AAE/C,QAAI,YAAa,MAAM,WAAY,KAAK,OAAO,qBAAqB;AAClE,aAAO;AAAA,IACT;AAEA,SAAK,aAAa,IAAI,UAAU,GAAG;AACnC,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,OAA2B;AAErD,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,WAAO,kBAAkB,SAAS,MAAM,IAAI;AAAA,EAC9C;AAAA,EAEQ,8BAAoC;AAE1C,SAAK,kBAAkB,gBAAgB;AAAA,MACrC,YAAY,CAAC,UAAU,MAAM,SAAS;AAAA,MACtC,SAAS,OAAO,UAAU;AACxB,eAAO,CAAC;AAAA,UACN,IAAI,UAAU,MAAM,EAAE;AAAA,UACtB;AAAA,UACA,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,UAAU,MAAM,QAAQ,YAAY;AAAA,YACpC,SAAS,MAAM,QAAQ,WAAW;AAAA,YAClC,UAAU,MAAM,QAAQ,cAAc;AAAA,UACxC;AAAA,UACA,UAAU;AAAA,YACR,WAAW,MAAM,QAAQ;AAAA,YACzB,gBAAgB,MAAM,QAAQ,kBAAkB;AAAA,UAClD;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,kBAAkB,qBAAqB;AAAA,MAC1C,YAAY,CAAC,UAAU,MAAM,SAAS;AAAA,MACtC,SAAS,OAAO,UAAU;AACxB,eAAO,CAAC;AAAA,UACN,IAAI,UAAU,MAAM,EAAE;AAAA,UACtB;AAAA,UACA,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,UAAU,MAAM,QAAQ,YAAY;AAAA,YACpC,SAAS,MAAM,QAAQ,WAAW;AAAA,UACpC;AAAA,UACA,UAAU;AAAA,YACR,WAAW,MAAM,QAAQ;AAAA,YACzB,cAAc,MAAM,QAAQ;AAAA,UAC9B;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,kBAAkB,kBAAkB;AAAA,MACvC,YAAY,CAAC,UAAU,MAAM,SAAS;AAAA,MACtC,SAAS,OAAO,UAAU;AACxB,eAAO,CAAC;AAAA,UACN,IAAI,UAAU,MAAM,EAAE;AAAA,UACtB;AAAA,UACA,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,UAAU,MAAM,QAAQ,YAAY;AAAA,YACpC,SAAS,MAAM,QAAQ,WAAW;AAAA,YAClC,WAAW,MAAM,QAAQ,aAAa;AAAA,UACxC;AAAA,UACA,UAAU;AAAA,YACR,WAAW,MAAM,QAAQ;AAAA,YACzB,cAAc,MAAM,QAAQ;AAAA,YAC5B,WAAW,MAAM,QAAQ;AAAA,UAC3B;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,kBAAkB,mBAAmB;AAAA,MACxC,YAAY,CAAC,UAAU,MAAM,SAAS,qBAAqB,MAAM,SAAS;AAAA,MAC1E,SAAS,OAAO,UAAU;AACxB,eAAO,CAAC;AAAA,UACN,IAAI,UAAU,MAAM,EAAE;AAAA,UACtB;AAAA,UACA,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,UAAU,MAAM,QAAQ,YAAY;AAAA,YACpC,SAAS,MAAM,QAAQ,WAAW;AAAA,YAClC,UAAU,MAAM,QAAQ,YAAY;AAAA,UACtC;AAAA,UACA,UAAU;AAAA,YACR,WAAW,MAAM,QAAQ;AAAA,YACzB,SAAS,MAAM,QAAQ;AAAA,YACvB,QAAQ,MAAM,SAAS;AAAA,UACzB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,kBAAkB,iBAAiB;AAAA,MACtC,YAAY,CAAC,UAAU,MAAM,SAAS;AAAA,MACtC,SAAS,OAAO,UAAU;AACxB,eAAO,CAAC;AAAA,UACN,IAAI,UAAU,MAAM,EAAE;AAAA,UACtB;AAAA,UACA,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,YAAY,MAAM,QAAQ,cAAc;AAAA,YACxC,UAAU,MAAM,QAAQ,YAAY;AAAA,YACpC,SAAS,MAAM,QAAQ,WAAW;AAAA,UACpC;AAAA,UACA,UAAU;AAAA,YACR,cAAc,MAAM,QAAQ;AAAA,YAC5B,SAAS,MAAM,QAAQ;AAAA,UACzB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK,kBAAkB,gBAAgB;AAAA,MACrC,YAAY,CAAC,UAAU,CAAC,gBAAgB,mBAAmB,EAAE,SAAS,MAAM,IAAI;AAAA,MAChF,SAAS,OAAO,UAAU;AACxB,eAAO,CAAC;AAAA,UACN,IAAI,kBAAkB,MAAM,EAAE;AAAA,UAC9B;AAAA,UACA,WAAW,MAAM;AAAA,UACjB,OAAO;AAAA,UACP,YAAY;AAAA,YACV,SAAS,MAAM,QAAQ,WAAW;AAAA,YAClC,UAAU,MAAM,QAAQ,YAAY;AAAA,UACtC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,qBAA2B;AACjC,gBAAY,YAAY;AACtB,UAAI;AACF,cAAM,KAAK,MAAM;AAAA,MACnB,SAAS,OAAO;AACd,aAAK,KAAK,eAAe,KAAK;AAAA,MAChC;AAAA,IACF,GAAG,KAAK,OAAO,aAAa;AAAA,EAC9B;AAAA,EAEQ,eAAqB;AAE3B,gBAAY,MAAM;AAChB,YAAM,SAAS,KAAK,IAAI,IAAI,KAAK,OAAO;AAExC,iBAAW,CAAC,KAAK,SAAS,KAAK,KAAK,aAAa,QAAQ,GAAG;AAC1D,YAAI,YAAY,QAAQ;AACtB,eAAK,aAAa,OAAO,GAAG;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,GAAG,KAAK,KAAK,GAAI;AAGjB,gBAAY,MAAM;AAChB,UAAI,KAAK,QAAQ,SAAS,KAAQ;AAChC,aAAK,UAAU,KAAK,QAAQ,MAAM,IAAM;AACxC,aAAK,KAAK,mBAAmB,EAAE,gBAAgB,KAAK,QAAQ,OAAO,CAAC;AAAA,MACtE;AAAA,IACF,GAAG,KAAK,KAAK,GAAI;AAAA,EACnB;AACF;;;ACzbA,IAAAC,iBAA6B;AA2BtB,IAAM,mBAAN,cAA+B,4BAAa;AAAA,EACzC;AAAA,EACA,eAAgD,oBAAI,IAAI;AAAA,EACxD,gBAAmE,oBAAI,IAAI;AAAA,EAC3E,oBAAmC,CAAC;AAAA,EAEpC,gBAAwC;AAAA,IAC9C,2BAA2B;AAAA,IAC3B,iBAAiB;AAAA,IACjB,gBAAgB,CAAC;AAAA,IACjB,gBAAgB,OAAO;AAAA;AAAA,IACvB,oBAAoB;AAAA,EACtB;AAAA,EAEA,YAAY,SAA0C,CAAC,GAAG;AACxD,UAAM;AACN,SAAK,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,OAAO;AACjD,SAAK,8BAA8B;AACnC,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,SAA4C;AAE/D,QAAI,CAAC,KAAK,eAAe,QAAQ,MAAM,GAAG;AACxC,YAAM,IAAI,MAAM,mCAAmC,QAAQ,MAAM,EAAE;AAAA,IACrE;AAGA,UAAM,KAAK,gBAAgB,OAAO;AAGlC,UAAM,cAAc,KAAK,UAAU,QAAQ,IAAI,EAAE;AACjD,QAAI,cAAc,KAAK,OAAO,gBAAgB;AAC5C,YAAM,IAAI,MAAM,gBAAgB,WAAW,oBAAoB,KAAK,OAAO,cAAc,EAAE;AAAA,IAC7F;AAGA,UAAM,SAAS,MAAM,KAAK,iBAAiB,OAAO;AAGlD,SAAK,kBAAkB,KAAK,OAAO;AAGnC,QAAI,KAAK,kBAAkB,SAAS,KAAM;AACxC,WAAK,oBAAoB,KAAK,kBAAkB,MAAM,IAAI;AAAA,IAC5D;AAEA,SAAK,KAAK,oBAAoB,EAAE,SAAS,YAAY,OAAO,OAAO,CAAC;AACpE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,MAAc,aAAuC;AACvE,SAAK,aAAa,IAAI,MAAM,WAAW;AACvC,SAAK,KAAK,0BAA0B,EAAE,KAAK,CAAC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAsB,MAAuB;AAC3C,UAAM,UAAU,KAAK,aAAa,OAAO,IAAI;AAC7C,QAAI,SAAS;AACX,WAAK,KAAK,4BAA4B,EAAE,KAAK,CAAC;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,OAA6B;AAChD,QAAI,CAAC,OAAO;AACV,aAAO,CAAC,GAAG,KAAK,iBAAiB;AAAA,IACnC;AAEA,WAAO,KAAK,kBAAkB,OAAO,OAAK,EAAE,aAAa,KAAK;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,kBAKE;AACA,UAAM,WAAmC,CAAC;AAC1C,UAAM,aAAa,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAI;AAEvD,QAAI,cAAc;AAClB,eAAW,WAAW,KAAK,mBAAmB;AAC5C,eAAS,QAAQ,MAAM,KAAK,SAAS,QAAQ,MAAM,KAAK,KAAK;AAE7D,UAAI,QAAQ,aAAa,YAAY;AACnC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,gBAAgB,KAAK,kBAAkB;AAAA,MACvC;AAAA,MACA;AAAA,MACA,kBAAkB,KAAK,aAAa;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAc,gBAAgB,SAAqC;AAEjE,QAAI,KAAK,OAAO,eAAe,SAAS,KAAK,CAAC,KAAK,OAAO,eAAe,SAAS,QAAQ,MAAM,GAAG;AACjG,YAAM,IAAI,MAAM,UAAU,QAAQ,MAAM,iBAAiB;AAAA,IAC3D;AAGA,QAAI,KAAK,OAAO,6BAA6B,KAAK,OAAO,WAAW;AAClE,YAAM,KAAK,kBAAkB,OAAO;AAAA,IACtC;AAGA,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,QAAI,CAAC,QAAQ,aAAa,EAAE,QAAQ,qBAAqB,OAAO;AAC9D,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,SAAqC;AACnE,UAAM,YAAY,QAAQ,QAAQ,KAAK,OAAO,eAAe,KAAK,QAAQ;AAE1E,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,gCAAgC,KAAK,OAAO,eAAe,EAAE;AAAA,IAC/E;AAGA,UAAM,oBAAoB,MAAM,KAAK,kBAAkB,QAAQ,MAAM,KAAK,OAAO,SAAU;AAE3F,QAAI,cAAc,mBAAmB;AACnC,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,SAAc,QAAiC;AAE7E,UAAM,aAAa,KAAK,UAAU,OAAO;AACzC,WAAO,UAAU,WAAW,MAAM,IAAI,OAAO,MAAM;AAAA,EACrD;AAAA,EAEQ,eAAe,QAAyB;AAC9C,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,KAAK,MAAM,OAAO,KAAK,IAAK;AAC9C,UAAM,eAAe,GAAG,MAAM,IAAI,SAAS;AAE3C,UAAM,UAAU,KAAK,cAAc,IAAI,YAAY,KAAK,EAAE,OAAO,GAAG,WAAW,MAAM,IAAM;AAE3F,QAAI,QAAQ,SAAS,KAAK,OAAO,oBAAoB;AACnD,aAAO;AAAA,IACT;AAEA,YAAQ;AACR,SAAK,cAAc,IAAI,cAAc,OAAO;AAC5C,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBAAiB,SAA4C;AACzE,UAAM,SAAsB,CAAC;AAE7B,eAAW,CAAC,MAAM,WAAW,KAAK,KAAK,aAAa,QAAQ,GAAG;AAC7D,UAAI;AACF,YAAI,YAAY,aAAa,OAAO,GAAG;AACrC,gBAAM,oBAAoB,MAAM,YAAY,UAAU,OAAO;AAC7D,iBAAO,KAAK,GAAG,iBAAiB;AAAA,QAClC;AAAA,MACF,SAAS,OAAO;AACd,aAAK,KAAK,qBAAqB,EAAE,iBAAiB,MAAM,SAAS,MAAM,CAAC;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,GAAG;AAEvB,aAAO,KAAK,MAAM,KAAK,iBAAiB,OAAO,CAAC;AAAA,IAClD;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBAAiB,SAA0C;AACvE,WAAO;AAAA,MACL,IAAI,WAAW,QAAQ,EAAE;AAAA,MACzB,MAAM,WAAW,QAAQ,MAAM;AAAA,MAC/B,WAAW,QAAQ;AAAA,MACnB,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,SAAS;AAAA,QACP,WAAW,QAAQ;AAAA,QACnB,WAAW,QAAQ,QAAQ,YAAY;AAAA,QACvC,WAAW,QAAQ,QAAQ,iBAAiB,KAAK,QAAQ,QAAQ,WAAW;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gCAAsC;AAE5C,SAAK,oBAAoB,gBAAgB;AAAA,MACvC,cAAc,CAAC,YAAY,QAAQ,OAAO,SAAS,KAAK,KAAK,QAAQ,OAAO,SAAS,UAAU;AAAA,MAC/F,WAAW,OAAO,YAAY;AAC5B,cAAM,SAAsB,CAAC;AAC7B,cAAM,OAAO,QAAQ;AAGrB,YAAI,KAAK,WAAW,UAAU,KAAK,WAAW,aAAa;AACzD,iBAAO,KAAK;AAAA,YACV,IAAI,iBAAiB,QAAQ,EAAE;AAAA,YAC/B,MAAM;AAAA,YACN,WAAW,QAAQ;AAAA,YACnB,QAAQ,QAAQ;AAAA,YAChB,SAAS;AAAA,cACP,WAAW,KAAK,aAAa,KAAK;AAAA,cAClC,UAAU,QAAQ;AAAA,cAClB,SAAS,KAAK,WAAW;AAAA,cACzB,iBAAiB,KAAK,MAAM,KAAK;AAAA,cACjC,cAAc,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,IAAI,QAAQ;AAAA,YACxE;AAAA,UACF,CAAC;AAAA,QACH;AAGA,YAAI,KAAK,WAAW,YAAY,KAAK,WAAW,SAAS;AACvD,iBAAO,KAAK;AAAA,YACV,IAAI,cAAc,QAAQ,EAAE;AAAA,YAC5B,MAAM;AAAA,YACN,WAAW,QAAQ;AAAA,YACnB,QAAQ,QAAQ;AAAA,YAChB,SAAS;AAAA,cACP,WAAW,KAAK,aAAa,KAAK;AAAA,cAClC,UAAU,QAAQ;AAAA,cAClB,SAAS,KAAK,WAAW;AAAA,cACzB,WAAW,KAAK,aAAa;AAAA,cAC7B,cAAc,KAAK,gBAAgB,KAAK;AAAA,cACxC,WAAW,KAAK,aAAa;AAAA,YAC/B;AAAA,UACF,CAAC;AAAA,QACH;AAEA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAGD,SAAK,oBAAoB,qBAAqB;AAAA,MAC5C,cAAc,CAAC,YAAY,QAAQ,OAAO,SAAS,UAAU,KAAK,QAAQ,OAAO,SAAS,OAAO;AAAA,MACjG,WAAW,OAAO,YAAY;AAC5B,cAAM,SAAsB,CAAC;AAC7B,cAAM,OAAO,QAAQ;AAGrB,YAAI,KAAK,cAAc,WAAW,KAAK,SAAS,gBAAgB;AAC9D,iBAAO,KAAK;AAAA,YACV,IAAI,kBAAkB,QAAQ,EAAE;AAAA,YAChC,MAAM;AAAA,YACN,WAAW,QAAQ;AAAA,YACnB,QAAQ,QAAQ;AAAA,YAChB,SAAS;AAAA,cACP,WAAW,KAAK;AAAA,cAChB,UAAU,QAAQ;AAAA,cAClB,SAAS;AAAA,cACT,UAAU,KAAK,cAAc;AAAA,cAC7B,SAAS,KAAK,aAAa,KAAK;AAAA,cAChC,YAAY,KAAK;AAAA,YACnB;AAAA,UACF,CAAC;AAAA,QACH;AAGA,YAAI,KAAK,iBAAiB,KAAK,QAAQ;AACrC,gBAAM,SAAS,KAAK,iBAAiB,KAAK;AAE1C,cAAI,CAAC,aAAa,MAAM,EAAE,SAAS,MAAM,GAAG;AAC1C,mBAAO,KAAK;AAAA,cACV,IAAI,sBAAsB,QAAQ,EAAE;AAAA,cACpC,MAAM;AAAA,cACN,WAAW,QAAQ;AAAA,cACnB,QAAQ,QAAQ;AAAA,cAChB,SAAS;AAAA,gBACP,WAAW,KAAK;AAAA,gBAChB,UAAU,QAAQ;AAAA,gBAClB,SAAS;AAAA,gBACT,cAAc,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,IAAI,QAAQ;AAAA,cACxE;AAAA,YACF,CAAC;AAAA,UACH,WAAW,CAAC,UAAU,UAAU,EAAE,SAAS,MAAM,GAAG;AAClD,mBAAO,KAAK;AAAA,cACV,IAAI,mBAAmB,QAAQ,EAAE;AAAA,cACjC,MAAM;AAAA,cACN,WAAW,QAAQ;AAAA,cACnB,QAAQ,QAAQ;AAAA,cAChB,SAAS;AAAA,gBACP,WAAW,KAAK;AAAA,gBAChB,UAAU,QAAQ;AAAA,gBAClB,SAAS;AAAA,gBACT,WAAW,KAAK,eAAe;AAAA,gBAC/B,cAAc,KAAK,iBAAiB;AAAA,cACtC;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAEA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAGD,SAAK,oBAAoB,qBAAqB;AAAA,MAC5C,cAAc,CAAC,YAAY;AACzB,cAAM,OAAO,QAAQ;AACrB,eAAO,SAAS,KAAK,aAAa,KAAK,OAAO,KAAK;AAAA,MACrD;AAAA,MACA,WAAW,OAAO,YAAY;AAC5B,cAAM,SAAsB,CAAC;AAC7B,cAAM,OAAO,QAAQ;AAErB,cAAM,cAAc;AAAA,UAClB,WAAW,KAAK,aAAa,KAAK;AAAA,UAClC,UAAU,QAAQ;AAAA,UAClB,SAAS,KAAK,WAAW;AAAA,QAC3B;AAEA,gBAAQ,KAAK,QAAQ;AAAA,UACnB,KAAK;AAAA,UACL,KAAK;AACH,mBAAO,KAAK;AAAA,cACV,IAAI,qBAAqB,QAAQ,EAAE;AAAA,cACnC,MAAM;AAAA,cACN,WAAW,QAAQ;AAAA,cACnB,QAAQ,QAAQ;AAAA,cAChB,SAAS;AAAA,gBACP,GAAG;AAAA,gBACH,cAAc,KAAK,cAAc,IAAI,KAAK,KAAK,WAAW,IAAI,QAAQ;AAAA,cACxE;AAAA,YACF,CAAC;AACD;AAAA,UAEF,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AACH,mBAAO,KAAK;AAAA,cACV,IAAI,kBAAkB,QAAQ,EAAE;AAAA,cAChC,MAAM;AAAA,cACN,WAAW,QAAQ;AAAA,cACnB,QAAQ,QAAQ;AAAA,cAChB,SAAS;AAAA,gBACP,GAAG;AAAA,gBACH,WAAW,KAAK,aAAa,KAAK,cAAc;AAAA,gBAChD,cAAc,KAAK,gBAAgB,KAAK,iBAAiB;AAAA,gBACzD,WAAW,KAAK,aAAa;AAAA,cAC/B;AAAA,YACF,CAAC;AACD;AAAA,UAEF,KAAK;AACH,mBAAO,KAAK;AAAA,cACV,IAAI,mBAAmB,QAAQ,EAAE;AAAA,cACjC,MAAM;AAAA,cACN,WAAW,QAAQ;AAAA,cACnB,QAAQ,QAAQ;AAAA,cAChB,SAAS;AAAA,gBACP,GAAG;AAAA,gBACH,SAAS,KAAK,cAAc,KAAK;AAAA,gBACjC,UAAU,KAAK,YAAY;AAAA,cAC7B;AAAA,YACF,CAAC;AACD;AAAA,QACJ;AAEA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,eAAqB;AAE3B,gBAAY,MAAM;AAChB,YAAM,MAAM,KAAK,IAAI;AAErB,iBAAW,CAAC,KAAK,IAAI,KAAK,KAAK,cAAc,QAAQ,GAAG;AACtD,YAAI,MAAM,KAAK,WAAW;AACxB,eAAK,cAAc,OAAO,GAAG;AAAA,QAC/B;AAAA,MACF;AAAA,IACF,GAAG,IAAI,KAAK,GAAI;AAAA,EAClB;AACF;;;ACzYO,IAAM,kBAAN,MAAsB;AAAA,EACnB;AAAA,EACA,iBAAwC,oBAAI,IAAI;AAAA,EAChD,mBAA0C,oBAAI,IAAI;AAAA,EAClD,YAAqF,oBAAI,IAAI;AAAA,EAE7F,gBAAwC;AAAA,IAC9C,YAAY;AAAA,MACV,EAAE,MAAM,UAAU,MAAM,eAAe,SAAS,MAAM,QAAQ,EAAE,WAAW,EAAE,EAAE;AAAA,MAC/E,EAAE,MAAM,OAAO,MAAM,eAAe,SAAS,MAAM,QAAQ,EAAE,YAAY,IAAI,EAAE;AAAA,MAC/E,EAAE,MAAM,aAAa,MAAM,MAAM,SAAS,OAAO,QAAQ,EAAE,eAAe,IAAI,EAAE;AAAA,IAClF;AAAA,IACA,aAAa;AAAA,IACb,eAAe;AAAA,IACf,qBAAqB;AAAA,IACrB,0BAA0B;AAAA,EAC5B;AAAA,EAEA,YAAY,SAA0C,CAAC,GAAG;AACxD,SAAK,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,OAAO;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,QAAwC;AACpE,UAAM,MAAM,KAAK,aAAa,OAAO,MAAM,OAAO,UAAU;AAC5D,UAAM,YAAuB,CAAC;AAG9B,SAAK,qBAAqB,KAAK,OAAO,KAAK;AAG3C,eAAW,aAAa,KAAK,OAAO,YAAY;AAC9C,UAAI,CAAC,UAAU,QAAS;AAExB,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,aAAa,QAAQ,WAAW,GAAG;AAC9D,YAAI,WAAW,QAAQ,cAAc,KAAK,OAAO,qBAAqB;AACpE,oBAAU,KAAK,OAAO;AAAA,QACxB;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,0CAA0C,UAAU,IAAI,KAAK,KAAK;AAAA,MAClF;AAAA,IACF;AAEA,WAAO,KAAK,4BAA4B,SAAS;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBACJ,SACA,YACoB;AACpB,UAAM,YAAuB,CAAC;AAG9B,UAAM,iBAAiB,KAAK,gCAAgC,OAAO;AAEnE,eAAW,CAAC,KAAK,WAAW,KAAK,eAAe,QAAQ,GAAG;AACzD,YAAM,CAAC,YAAY,aAAa,IAAI,IAAI,MAAM,GAAG;AACjD,YAAM,aAAa,KAAK,MAAM,aAAa;AAG3C,YAAM,aAAa,YAChB,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC,EAC5D,IAAI,QAAM,EAAE,WAAW,EAAE,WAAW,OAAO,EAAE,aAAa,IAAI,EAAE;AAEnE,UAAI,WAAW,SAAS,KAAK,OAAO,eAAe;AACjD;AAAA,MACF;AAGA,UAAI,iBAAiB;AACrB,UAAI,KAAK,OAAO,0BAA0B;AACxC,yBAAiB,MAAM,KAAK,qBAAqB,YAAY,GAAG;AAAA,MAClE;AAGA,eAAS,IAAI,KAAK,MAAM,eAAe,SAAS,GAAG,GAAG,IAAI,eAAe,QAAQ,KAAK;AACpF,cAAM,QAAQ,eAAe,CAAC;AAC9B,cAAM,mBAAmB,eAAe,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC;AAEpE,YAAI,iBAAiB,SAAS,KAAK,OAAO,cAAe;AAEzD,cAAM,aAAyB;AAAA,UAC7B,IAAI,SAAS,CAAC;AAAA,UACd,MAAM;AAAA,UACN,WAAW,MAAM;AAAA,UACjB,OAAO,MAAM;AAAA,UACb;AAAA,QACF;AAEA,cAAM,iBAAiB,MAAM,KAAK,wBAAwB,UAAU;AACpE,kBAAU,KAAK,GAAG,cAAc;AAAA,MAClC;AAAA,IACF;AAEA,WAAO,KAAK,4BAA4B,SAAS;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,SACA,aAAqB,IACG;AACxB,UAAM,WAA0B,CAAC;AACjC,UAAM,iBAAiB,KAAK,gCAAgC,OAAO;AAEnE,eAAW,CAAC,KAAK,WAAW,KAAK,eAAe,QAAQ,GAAG;AACzD,YAAM,CAAC,YAAY,aAAa,IAAI,IAAI,MAAM,GAAG;AACjD,YAAM,aAAa,KAAK,MAAM,aAAa;AAE3C,YAAM,gBAAgB,YAAY,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAE9F,UAAI,cAAc,SAAS,aAAa,EAAG;AAG3C,eAAS,IAAI,YAAY,IAAI,cAAc,SAAS,YAAY,KAAK;AACnE,cAAM,eAAe,cAAc,MAAM,IAAI,YAAY,CAAC;AAC1D,cAAM,cAAc,cAAc,MAAM,GAAG,IAAI,UAAU;AAEzD,cAAM,cAAc,KAAK,eAAe,aAAa,IAAI,OAAK,EAAE,aAAa,GAAG,CAAC;AACjF,cAAM,aAAa,KAAK,eAAe,YAAY,IAAI,OAAK,EAAE,aAAa,GAAG,CAAC;AAG/E,cAAM,cAAc,KAAK,IAAI,aAAa,WAAW;AACrD,cAAM,wBAAwB,KAAK,wBAAwB,OAAO;AAElE,YAAI,cAAc,uBAAuB;AACvC,mBAAS,KAAK;AAAA,YACZ,IAAI,gBAAgB,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAAA,YACzE,MAAM;AAAA,YACN,OAAO,4BAA4B,UAAU;AAAA,YAC7C,aAAa,iCAAiC,YAAY,QAAQ,CAAC,CAAC,OAAO,WAAW,QAAQ,CAAC,CAAC;AAAA,YAChG,UAAU,KAAK,uBAAuB,aAAa,qBAAqB;AAAA,YACxE,QAAQ;AAAA,YACR;AAAA,YACA,OAAO;AAAA,YACP,eAAe;AAAA,YACf,YAAY,KAAK,IAAI,MAAM,MAAO,cAAc,wBAAyB,GAAG;AAAA,YAC5E,YAAY,cAAc,wBAAwB;AAAA,YAClD,iBAAiB,KAAK,6BAA6B,aAAa,YAAY,UAAwB;AAAA,YACpG,YAAY,cAAc,CAAC,EAAE;AAAA,UAC/B,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,SAA4C;AAChE,UAAM,iBAAiB,KAAK,gCAAgC,OAAO;AAEnE,eAAW,CAAC,KAAK,WAAW,KAAK,eAAe,QAAQ,GAAG;AACzD,YAAM,SAAS,YAAY,IAAI,OAAK,EAAE,aAAa,GAAG;AAEtD,UAAI,OAAO,SAAS,KAAK,OAAO,cAAe;AAE/C,YAAM,OAAO,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,OAAO;AAC5D,YAAM,WAAW,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO;AACpF,YAAM,SAAS,KAAK,KAAK,QAAQ;AACjC,YAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,YAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAE9B,WAAK,UAAU,IAAI,KAAK,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAc,aACZ,QACA,WACA,KACyB;AACzB,UAAM,mBAAmB,KAAK,eAAe,IAAI,GAAG,KAAK,CAAC;AAE1D,QAAI,iBAAiB,SAAS,KAAK,OAAO,eAAe;AACvD,aAAO;AAAA,IACT;AAEA,YAAQ,UAAU,MAAM;AAAA,MACtB,KAAK;AACH,eAAO,KAAK,gBAAgB,QAAQ,kBAAkB,UAAU,QAAQ,GAAG;AAAA,MAE7E,KAAK;AACH,eAAO,KAAK,aAAa,QAAQ,kBAAkB,UAAU,QAAQ,GAAG;AAAA,MAE1E,KAAK;AACH,eAAO,KAAK,yBAAyB,QAAQ,kBAAkB,UAAU,QAAQ,GAAG;AAAA,MAEtF,KAAK;AACH,eAAO,KAAK,mBAAmB,QAAQ,UAAU,QAAQ,GAAG;AAAA,MAE9D;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEQ,gBACN,QACA,kBACA,QACA,KACgB;AAChB,UAAM,OAAO,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,iBAAiB;AAChF,UAAM,WAAW,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,iBAAiB;AACxG,UAAM,SAAS,KAAK,KAAK,QAAQ;AAEjC,QAAI,WAAW,EAAG,QAAO;AAEzB,UAAM,SAAS,KAAK,KAAK,OAAO,QAAQ,QAAQ,MAAM;AACtD,UAAM,YAAY,OAAO,aAAa;AAEtC,QAAI,SAAS,WAAW;AACtB,YAAM,WAAW,KAAK,kBAAkB,QAAQ,SAAS;AACzD,YAAM,aAAa,KAAK,IAAI,MAAM,SAAS,YAAY,GAAG;AAE1D,aAAO;AAAA,QACL,IAAI,UAAU,OAAO,EAAE;AAAA,QACvB,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,OAAO,OAAO;AAAA,QACd,eAAe;AAAA,QACf,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,YAAY,OAAO;AAAA,QACnB,SAAS;AAAA,UACP,OAAO,KAAK,YAAY,gBAAgB;AAAA,UACxC,aAAa;AAAA,UACb,uBAAwB,OAAO,QAAQ,QAAQ,OAAQ;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,aACN,QACA,kBACA,QACA,KACgB;AAChB,UAAM,eAAe,CAAC,GAAG,gBAAgB,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/D,UAAM,UAAU,KAAK,MAAM,aAAa,SAAS,IAAI;AACrD,UAAM,UAAU,KAAK,MAAM,aAAa,SAAS,IAAI;AAErD,UAAM,KAAK,aAAa,OAAO;AAC/B,UAAM,KAAK,aAAa,OAAO;AAC/B,UAAM,MAAM,KAAK;AAEjB,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,aAAa,KAAK,aAAa;AACrC,UAAM,aAAa,KAAK,aAAa;AAErC,QAAI,OAAO,QAAQ,cAAc,OAAO,QAAQ,YAAY;AAC1D,YAAM,iBAAiB,KAAK,MAAM;AAClC,YAAM,YAAY,KAAK,IAAI,OAAO,QAAQ,aAAa,IAAI;AAC3D,YAAM,WAAW,KAAK,kBAAkB,WAAW,UAAU;AAC7D,YAAM,aAAa,KAAK,IAAI,MAAM,YAAY,aAAa,GAAG;AAE9D,aAAO;AAAA,QACL,IAAI,OAAO,OAAO,EAAE;AAAA,QACpB,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,OAAO,OAAO;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,YAAY,OAAO;AAAA,QACnB,SAAS;AAAA,UACP,OAAO,KAAK,YAAY,gBAAgB;AAAA,UACxC,aAAa;AAAA,UACb,uBAAwB,OAAO,QAAQ,iBAAiB,gBAAiB;AAAA,QAC3E;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,yBACN,QACA,kBACA,QACA,KACgB;AAEhB,UAAM,gBAAgB,OAAO,iBAAiB;AAC9C,UAAM,YAAY,KAAK,4BAA4B,kBAAkB,aAAa;AAElF,UAAM,eAAe,KAAK,wBAAwB,OAAO,OAAO,gBAAgB;AAEhF,QAAI,eAAe,WAAW;AAC5B,YAAM,gBAAgB,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,iBAAiB;AACzF,YAAM,WAAW,KAAK,kBAAkB,cAAc,SAAS;AAC/D,YAAM,aAAa,KAAK,IAAI,MAAM,eAAe,YAAY,GAAG;AAEhE,aAAO;AAAA,QACL,IAAI,aAAa,OAAO,EAAE;AAAA,QAC1B,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,OAAO,OAAO;AAAA,QACd;AAAA,QACA,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,YAAY,OAAO;AAAA,MACrB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,mBACN,QACA,QACA,KACgB;AAChB,UAAM,iBAAiB,OAAO;AAC9B,UAAM,iBAAiB,OAAO;AAE9B,QAAI,mBAAmB,UAAa,OAAO,QAAQ,gBAAgB;AACjE,aAAO;AAAA,QACL,IAAI,mBAAmB,OAAO,EAAE;AAAA,QAChC,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,OAAO,OAAO;AAAA,QACd,eAAe;AAAA,QACf,YAAY,OAAO,QAAQ,kBAAkB;AAAA,QAC7C,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,YAAY,OAAO;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,mBAAmB,UAAa,OAAO,QAAQ,gBAAgB;AACjE,aAAO;AAAA,QACL,IAAI,mBAAmB,OAAO,EAAE;AAAA,QAChC,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,OAAO,OAAO;AAAA,QACd,eAAe;AAAA,QACf,YAAY,iBAAiB,OAAO,SAAS;AAAA,QAC7C,UAAU;AAAA,QACV,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,YAAY,OAAO;AAAA,MACrB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,WAAmB,WAA2D;AACtG,UAAM,QAAQ,YAAY;AAE1B,QAAI,QAAQ,EAAG,QAAO;AACtB,QAAI,QAAQ,EAAG,QAAO;AACtB,QAAI,QAAQ,IAAK,QAAO;AACxB,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,QAAgB,WAA2D;AACxG,UAAM,QAAQ,SAAS;AAEvB,QAAI,QAAQ,EAAG,QAAO;AACtB,QAAI,QAAQ,EAAG,QAAO;AACtB,QAAI,QAAQ,EAAG,QAAO;AACtB,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,MAAkB,YAA4C;AACjF,WAAO,GAAG,IAAI,IAAI,KAAK,UAAU,UAAU,CAAC;AAAA,EAC9C;AAAA,EAEQ,qBAAqB,KAAa,OAAqB;AAC7D,UAAM,UAAU,KAAK,eAAe,IAAI,GAAG,KAAK,CAAC;AACjD,YAAQ,KAAK,KAAK;AAGlB,QAAI,QAAQ,SAAS,KAAK;AACxB,cAAQ,OAAO,GAAG,QAAQ,SAAS,GAAG;AAAA,IACxC;AAEA,SAAK,eAAe,IAAI,KAAK,OAAO;AAAA,EACtC;AAAA,EAEQ,gCAAgC,SAA8D;AACpG,UAAM,UAAU,oBAAI,IAAgC;AAEpD,eAAW,UAAU,SAAS;AAC5B,YAAM,MAAM,GAAG,OAAO,IAAI,IAAI,KAAK,UAAU,OAAO,UAAU,CAAC;AAC/D,UAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,gBAAQ,IAAI,KAAK,CAAC,CAAC;AAAA,MACrB;AACA,cAAQ,IAAI,GAAG,EAAG,KAAK,MAAM;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,qBACZ,YACA,KAC+C;AAE/C,UAAM,aAAa,KAAK,IAAI,IAAI,KAAK,MAAM,WAAW,SAAS,CAAC,CAAC;AAEjE,QAAI,aAAa,EAAG,QAAO;AAE3B,WAAO,WAAW,IAAI,CAAC,OAAO,UAAU;AACtC,YAAM,cAAc,KAAK,IAAI,GAAG,QAAQ,KAAK,MAAM,aAAa,CAAC,CAAC;AAClE,YAAM,YAAY,KAAK,IAAI,WAAW,QAAQ,cAAc,UAAU;AACtE,YAAM,SAAS,WAAW,MAAM,aAAa,SAAS;AAEtD,YAAM,cAAc,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAAI,OAAO;AACzE,YAAM,gBAAgB,MAAM,QAAQ,cAAc,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,IAAI,WAAW;AAE/G,aAAO;AAAA,QACL,WAAW,MAAM;AAAA,QACjB,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,eAAe,QAA0B;AAC/C,QAAI,OAAO,SAAS,EAAG,QAAO;AAE9B,UAAM,IAAI,OAAO;AACjB,UAAM,IAAI,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC;AAC/C,UAAM,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACxC,UAAM,OAAO,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAC7C,UAAM,QAAQ,EAAE,OAAO,CAAC,KAAK,IAAI,MAAM,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC;AAC9D,UAAM,QAAQ,EAAE,OAAO,CAAC,KAAK,OAAO,MAAM,KAAK,IAAI,CAAC;AAEpD,YAAQ,IAAI,QAAQ,OAAO,SAAS,IAAI,QAAQ,OAAO;AAAA,EACzD;AAAA,EAEQ,YAAY,QAA0D;AAC5E,UAAM,QAAQ,KAAK,eAAe,MAAM;AACxC,UAAM,YAAY;AAElB,QAAI,KAAK,IAAI,KAAK,IAAI,UAAW,QAAO;AACxC,WAAO,QAAQ,IAAI,eAAe;AAAA,EACpC;AAAA,EAEQ,wBAAwB,MAAmC;AACjE,UAAM,iBAAiB;AAAA,MACrB,SAAS,EAAE,KAAK,KAAK,QAAQ,KAAK,MAAM,IAAI;AAAA,MAC5C,OAAO,EAAE,KAAK,GAAK,QAAQ,GAAK,MAAM,IAAI;AAAA,IAC5C;AAEA,WAAO,eAAe,IAAI,EAAE,KAAK,OAAO,WAAW;AAAA,EACrD;AAAA,EAEQ,4BAA4B,QAAkB,eAA+B;AAEnF,UAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/C,UAAM,cAAc,KAAK,OAAO,IAAI,iBAAiB,OAAO,MAAM;AAClE,WAAO;AAAA,EACT;AAAA,EAEQ,wBAAwB,OAAe,kBAAoC;AAEjF,UAAM,OAAO,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,iBAAiB;AAChF,UAAM,YAAY,iBAAiB,IAAI,OAAK,KAAK,IAAI,IAAI,KAAK,CAAC;AAC/D,UAAM,cAAc,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,UAAU;AAEzE,WAAO,eAAe,KAAK,IAAI,QAAQ,IAAI,IAAI;AAAA,EACjD;AAAA,EAEQ,4BAA4B,WAAiC;AAEnE,UAAM,SAAS,oBAAI,IAAqB;AAExC,eAAW,WAAW,WAAW;AAC/B,YAAM,MAAM,GAAG,QAAQ,UAAU,IAAI,KAAK,UAAU,QAAQ,UAAU,CAAC,IAAI,QAAQ,UAAU,YAAY,CAAC;AAE1G,UAAI,CAAC,OAAO,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,EAAG,aAAa,QAAQ,YAAY;AACxE,eAAO,IAAI,KAAK,OAAO;AAAA,MACzB;AAAA,IACF;AAEA,WAAO,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AAAA,EAC/E;AAAA,EAEQ,6BACN,aACA,YACA,YACU;AACV,UAAM,kBAA4B,CAAC;AACnC,UAAM,eAAe,aAAa;AAClC,UAAM,SAAS,KAAK,IAAI,aAAa,WAAW;AAEhD,YAAQ,YAAY;AAAA,MAClB;AACE,YAAI,cAAc;AAChB,0BAAgB;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA;AAAA,MAEF;AACE,YAAI,CAAC,cAAc;AACjB,0BAAgB;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA;AAAA,MAEF;AACE,YAAI,cAAc;AAChB,0BAAgB;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,OAAO;AACL,0BAAgB;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA;AAAA,IACJ;AAEA,QAAI,SAAS,GAAK;AAChB,sBAAgB,KAAK,4DAA4D;AAAA,IACnF;AAEA,WAAO;AAAA,EACT;AACF;;;ACrgBO,IAAM,uBAAN,MAA2B;AAAA,EACxB;AAAA,EACA,kBAA+C,oBAAI,IAAI;AAAA,EACvD,uBAA4C,oBAAI,IAAI;AAAA,EAEpD,gBAAsC;AAAA,IAC5C,OAAO,CAAC;AAAA,IACR,uBAAuB;AAAA,IACvB,qBAAqB;AAAA,IACrB,oBAAoB;AAAA,IACpB,YAAY;AAAA,MACV,EAAE,IAAI,QAAQ,MAAM,qBAAqB,aAAa,4BAA4B,QAAQ,IAAI;AAAA,MAC9F,EAAE,IAAI,eAAe,MAAM,eAAe,aAAa,8BAA8B,QAAQ,IAAI;AAAA,MACjG,EAAE,IAAI,eAAe,MAAM,eAAe,aAAa,8BAA8B,QAAQ,EAAI;AAAA,MACjG,EAAE,IAAI,YAAY,MAAM,YAAY,aAAa,+BAA+B,QAAQ,KAAK;AAAA,IAC/F;AAAA,EACF;AAAA,EAEA,YAAY,SAAwC,CAAC,GAAG;AACtD,SAAK,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,OAAO;AACjD,SAAK,uBAAuB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,SAAwD;AACpF,UAAM,kBAAoC,CAAC;AAG3C,UAAM,2BAA2B,MAAM,KAAK,iCAAiC,OAAO;AACpF,oBAAgB,KAAK,GAAG,wBAAwB;AAGhD,UAAM,8BAA8B,MAAM,KAAK,oCAAoC,OAAO;AAC1F,oBAAgB,KAAK,GAAG,2BAA2B;AAGnD,UAAM,iCAAiC,MAAM,KAAK,uCAAuC,OAAO;AAChG,oBAAgB,KAAK,GAAG,8BAA8B;AAGtD,QAAI,KAAK,OAAO,uBAAuB;AACrC,YAAM,oBAAoB,MAAM,KAAK,+BAA+B,OAAO;AAC3E,sBAAgB,KAAK,GAAG,iBAAiB;AAAA,IAC3C;AAGA,UAAM,0BAA0B,KAAK,yBAAyB,eAAe;AAG7E,eAAW,kBAAkB,yBAAyB;AACpD,WAAK,gBAAgB,IAAI,eAAe,IAAI,cAAc;AAAA,IAC5D;AAEA,WAAO,wBAAwB,MAAM,GAAG,KAAK,OAAO,kBAAkB;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA,EAKA,6BAA6B,UAAoC;AAC/D,WAAO,MAAM,KAAK,KAAK,gBAAgB,OAAO,CAAC,EAC5C,OAAO,OAAK,EAAE,aAAa,QAAQ,EACnC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA,EAKA,gCAAgC,kBAAmC;AACjE,UAAM,iBAAiB,KAAK,gBAAgB,IAAI,gBAAgB;AAChE,QAAI,CAAC,eAAgB,QAAO;AAG5B,SAAK,gBAAgB,OAAO,gBAAgB;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAsB,kBAA0B,QAA0B;AACxE,UAAM,iBAAiB,KAAK,gBAAgB,IAAI,gBAAgB;AAChE,QAAI,CAAC,eAAgB,QAAO;AAG5B,SAAK,gBAAgB,OAAO,gBAAgB;AAC5C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,yBAKE;AACA,UAAM,kBAAkB,MAAM,KAAK,KAAK,gBAAgB,OAAO,CAAC;AAChE,UAAM,aAAqC,CAAC;AAC5C,UAAM,WAAmC,CAAC;AAC1C,UAAM,aAAqC,CAAC;AAE5C,eAAW,OAAO,iBAAiB;AACjC,iBAAW,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,KAAK;AAC7D,eAAS,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK;AAErD,YAAM,gBAAgB,IAAI,YAAY,IAAI,SAAS,IAAI,YAAY,IAAI,WAAW;AAClF,iBAAW,aAAa,KAAK,WAAW,aAAa,KAAK,KAAK;AAAA,IACjE;AAEA,WAAO;AAAA,MACL,OAAO,gBAAgB;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,iCAAiC,SAAwD;AACrG,UAAM,kBAAoC,CAAC;AAE3C,eAAW,QAAQ,KAAK,OAAO,OAAO;AACpC,UAAI,CAAC,KAAK,QAAS;AAEnB,UAAI;AACF,cAAM,kBAAkB,KAAK,uBAAuB,MAAM,OAAO;AAEjE,YAAI,gBAAgB,SAAS,GAAG;AAC9B,gBAAM,iBAAiB,MAAM,KAAK,6BAA6B,MAAM,eAAe;AACpF,cAAI,kBAAkB,eAAe,cAAc,KAAK,OAAO,qBAAqB;AAClF,4BAAgB,KAAK,cAAc;AACnC,iBAAK,oBAAoB,KAAK,EAAE;AAAA,UAClC;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,kCAAkC,KAAK,EAAE,KAAK,KAAK;AAAA,MACnE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,oCAAoC,SAAwD;AACxG,UAAM,kBAAoC,CAAC;AAG3C,UAAM,eAAe,KAAK,oBAAoB,OAAO;AACrD,oBAAgB,KAAK,GAAG,KAAK,iCAAiC,YAAY,CAAC;AAG3E,UAAM,kBAAkB,KAAK,uBAAuB,OAAO;AAC3D,oBAAgB,KAAK,GAAG,KAAK,oCAAoC,eAAe,CAAC;AAGjF,UAAM,gBAAgB,KAAK,qBAAqB,OAAO;AACvD,oBAAgB,KAAK,GAAG,KAAK,kCAAkC,aAAa,CAAC;AAE7E,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,uCAAuC,SAAwD;AAC3G,UAAM,kBAAoC,CAAC;AAG3C,UAAM,qBAAqB,KAAK,2BAA2B,OAAO;AAClE,oBAAgB,KAAK,GAAG,KAAK,gCAAgC,kBAAkB,CAAC;AAGhF,UAAM,oBAAoB,KAAK,yBAAyB,OAAO;AAC/D,oBAAgB,KAAK,GAAG,KAAK,yCAAyC,iBAAiB,CAAC;AAExF,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,+BAA+B,SAAwD;AAInG,UAAM,kBAAoC,CAAC;AAG3C,UAAM,0BAA0B,MAAM,KAAK,gCAAgC,OAAO;AAClF,oBAAgB,KAAK,GAAG,uBAAuB;AAE/C,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,MAA0B,SAAiD;AACxG,UAAM,kBAAsC,CAAC;AAE7C,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,kBAAkB,QAAQ,OAAO,OAAK;AAE1C,YAAI,EAAE,SAAS,UAAU,OAAQ,QAAO;AAGxC,YAAI,UAAU,YAAY;AACxB,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,UAAU,UAAU,GAAG;AAC/D,gBAAI,EAAE,WAAW,GAAG,MAAM,MAAO,QAAO;AAAA,UAC1C;AAAA,QACF;AAEA,eAAO;AAAA,MACT,CAAC;AAGD,iBAAW,UAAU,iBAAiB;AACpC,YAAI,KAAK,kBAAkB,QAAQ,SAAS,GAAG;AAC7C,0BAAgB,KAAK,MAAM;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,QAA0B,WAAmC;AACrF,UAAM,QAAQ,OAAO,aAAa;AAElC,YAAQ,UAAU,UAAU;AAAA,MAC1B,KAAK;AAAM,eAAO,QAAS,UAAU;AAAA,MACrC,KAAK;AAAM,eAAO,QAAS,UAAU;AAAA,MACrC,KAAK;AAAO,eAAO,SAAU,UAAU;AAAA,MACvC,KAAK;AAAO,eAAO,SAAU,UAAU;AAAA,MACvC,KAAK;AAAM,eAAO,UAAW,UAAU;AAAA,MACvC,KAAK;AACH,cAAM,CAAC,KAAK,GAAG,IAAI,UAAU;AAC7B,eAAO,SAAS,OAAO,SAAS;AAAA,MAClC,KAAK;AAEH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAc,6BACZ,MACA,iBACgC;AAChC,QAAI,KAAK,QAAQ,WAAW,EAAG,QAAO;AAEtC,UAAM,aAAa,KAAK,wBAAwB,MAAM,eAAe;AACrE,UAAM,SAAS,KAAK,yBAAyB,KAAK,OAAO;AACzD,UAAM,SAAS,KAAK,yBAAyB,KAAK,OAAO;AAEzD,WAAO;AAAA,MACL,IAAI,QAAQ,KAAK,EAAE,IAAI,KAAK,IAAI,CAAC;AAAA,MACjC,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,aAAa,wBAAwB,gBAAgB,MAAM;AAAA,MAC3D,WAAW,KAAK,kBAAkB,MAAM,eAAe;AAAA,MACvD,SAAS,KAAK;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA;AAAA,MACzD,UAAU;AAAA,QACR,SAAS,CAAC,KAAK,EAAE;AAAA,QACjB,aAAa;AAAA,UACX,SAAS,gBAAgB,IAAI,QAAM;AAAA,YACjC,MAAM,EAAE;AAAA,YACR,OAAO,EAAE,aAAa;AAAA,YACtB,WAAW,EAAE;AAAA,UACf,EAAE;AAAA,UACF,YAAY,KAAK,WAAW,IAAI,OAAK,GAAG,EAAE,MAAM,IAAI,EAAE,QAAQ,IAAI,EAAE,KAAK,EAAE;AAAA,QAC7E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBAAoB,SAAkC;AAE5D,UAAM,cAAc,oBAAI,IAAoB;AAC5C,UAAM,aAAa,oBAAI,IAAoB;AAE3C,eAAW,UAAU,SAAS;AAC5B,YAAM,OAAO,OAAO,UAAU,SAAS;AACvC,YAAM,YAAY,OAAO,UAAU,OAAO;AAE1C,kBAAY,IAAI,OAAO,YAAY,IAAI,IAAI,KAAK,KAAK,OAAO,aAAa,GAAG;AAC5E,iBAAW,IAAI,YAAY,WAAW,IAAI,SAAS,KAAK,KAAK,OAAO,aAAa,GAAG;AAAA,IACtF;AAEA,WAAO,EAAE,aAAa,WAAW;AAAA,EACnC;AAAA,EAEQ,iCAAiC,cAAqC;AAC5E,UAAM,kBAAoC,CAAC;AAC3C,UAAM,EAAE,aAAa,WAAW,IAAI;AAGpC,UAAM,WAAW,MAAM,KAAK,YAAY,QAAQ,CAA+B,EAAE;AAAA,MAAO,CAAC,KAAuB,SAC9G,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,OAAO;AAAA,MAAK,CAAC,GAAG,CAAC;AAAA,IACtC;AAEA,QAAI,SAAS,CAAC,IAAI,GAAG;AACnB,sBAAgB,KAAK;AAAA,QACnB,IAAI,aAAa,KAAK,IAAI,CAAC;AAAA,QAC3B,UAAU;AAAA,QACV,UAAU;AAAA,QACV,OAAO;AAAA,QACP,aAAa,wBAAwB,SAAS,CAAC,CAAC;AAAA,QAChD,WAAW;AAAA,QACX,SAAS,CAAC;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,CAAC;AAAA,QACD,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,WAAW,oBAAI,KAAK;AAAA,QACpB,UAAU;AAAA,UACR,SAAS,CAAC;AAAA,UACV,aAAa;AAAA,YACX,SAAS,CAAC;AAAA,YACV,YAAY,CAAC,oBAAoB,SAAS,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,KAAK;AAAA,UACrE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,SAAkC;AAC/D,UAAM,eAAe,oBAAI,IAAiE;AAE1F,eAAW,UAAU,SAAS;AAC5B,YAAM,UAAU,OAAO,WAAW,WAAW;AAC7C,YAAM,QAAQ,aAAa,IAAI,OAAO,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,EAAE;AAE9E,UAAI,OAAO,4CAAkC;AAC3C,cAAM,QAAQ,OAAO,aAAa;AAAA,MACpC,WAAW,OAAO,sDAAuC;AACvD,cAAM,aAAa,OAAO,aAAa;AAAA,MACzC,WAAW,OAAO,gDAAoC;AACpD,cAAM,UAAU,OAAO,aAAa;AAAA,MACtC;AAEA,mBAAa,IAAI,SAAS,KAAK;AAAA,IACjC;AAEA,WAAO,EAAE,aAAa;AAAA,EACxB;AAAA,EAEQ,oCAAoC,iBAAwC;AAClF,UAAM,kBAAoC,CAAC;AAC3C,UAAM,EAAE,aAAa,IAAI;AAEzB,eAAW,CAAC,SAAS,KAAK,KAAK,aAAa,QAAQ,GAAG;AACrD,YAAM,eAAe,MAAM,OAAO,IAAK,MAAM,YAAY,MAAM,OAAQ,MAAM;AAC7E,YAAM,cAAc,MAAM,OAAO,IAAK,MAAM,SAAS,MAAM,OAAQ,MAAM;AAEzE,UAAI,eAAe,MAAM,MAAM,OAAO,KAAK;AACzC,wBAAgB,KAAK;AAAA,UACnB,IAAI,oBAAoB,OAAO,IAAI,KAAK,IAAI,CAAC;AAAA,UAC7C,UAAU;AAAA,UACV,UAAU;AAAA,UACV,OAAO,WAAW,OAAO;AAAA,UACzB,aAAa,GAAG,OAAO,gBAAgB,aAAa,QAAQ,CAAC,CAAC;AAAA,UAC9D,WAAW;AAAA,UACX,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa,uCAAuC,OAAO;AAAA,YAC3D,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,OAAO;AAAA,cACL,UAAU,OAAO;AAAA,cACjB;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AAAA,UACD,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,WAAW,oBAAI,KAAK;AAAA,UACpB,UAAU;AAAA,YACR,SAAS,CAAC;AAAA,YACV,aAAa;AAAA,cACX,SAAS,CAAC;AAAA,cACV,YAAY,CAAC,GAAG,OAAO,mBAAmB,aAAa,QAAQ,CAAC,CAAC,GAAG;AAAA,YACtE;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,SAAkC;AAC7D,UAAM,eAAe,oBAAI,IAAoB;AAC7C,UAAM,mBAAmB,oBAAI,IAAoB;AAEjD,eAAW,UAAU,SAAS;AAC5B,UAAI,OAAO,gDAAoC;AAC7C,cAAM,YAAY,OAAO,WAAW,aAAa;AACjD,cAAM,WAAW,OAAO,WAAW,YAAY;AAE/C,qBAAa,IAAI,YAAY,aAAa,IAAI,SAAS,KAAK,KAAK,OAAO,aAAa,GAAG;AACxF,yBAAiB,IAAI,WAAW,iBAAiB,IAAI,QAAQ,KAAK,KAAK,OAAO,aAAa,GAAG;AAAA,MAChG;AAAA,IACF;AAEA,WAAO,EAAE,cAAc,iBAAiB;AAAA,EAC1C;AAAA,EAEQ,kCAAkC,eAAsC;AAC9E,UAAM,kBAAoC,CAAC;AAC3C,UAAM,EAAE,cAAc,iBAAiB,IAAI;AAG3C,QAAI,aAAa,OAAO,GAAG;AACzB,YAAM,WAAW,MAAM,KAAK,aAAa,QAAQ,CAA+B,EAAE;AAAA,QAAO,CAAC,KAAuB,SAC/G,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,OAAO;AAAA,MAC5B;AAEA,UAAI,SAAS,CAAC,IAAI,IAAI;AACpB,wBAAgB,KAAK;AAAA,UACnB,IAAI,SAAS,SAAS,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;AAAA,UACtC,UAAU;AAAA,UACV,UAAU;AAAA,UACV,OAAO,2BAA2B,SAAS,CAAC,CAAC;AAAA,UAC7C,aAAa,cAAc,SAAS,CAAC,CAAC,aAAa,SAAS,CAAC,CAAC;AAAA,UAC9D,WAAW;AAAA,UACX,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa,sCAAsC,SAAS,CAAC,CAAC;AAAA,YAC9D,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,OAAO;AAAA,cACL;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AAAA,UACD,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,WAAW,oBAAI,KAAK;AAAA,UACpB,UAAU;AAAA,YACR,SAAS,CAAC;AAAA,YACV,aAAa;AAAA,cACX,SAAS,CAAC;AAAA,cACV,YAAY,CAAC,SAAS,SAAS,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,cAAc;AAAA,YACjE;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,2BAA2B,SAAkC;AACnE,UAAM,gBAAgB,oBAAI,IAA0E;AAEpG,eAAW,UAAU,SAAS;AAC5B,YAAM,WAAW,OAAO,WAAW;AACnC,UAAI,CAAC,SAAU;AAEf,YAAM,QAAQ,cAAc,IAAI,QAAQ,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,iBAAiB,EAAE;AAEzF,UAAI,OAAO,4CAAkC;AAC3C,cAAM,QAAQ,OAAO,aAAa;AAAA,MACpC,WAAW,OAAO,sDAAuC;AACvD,cAAM,aAAa,OAAO,aAAa;AAAA,MACzC;AAEA,oBAAc,IAAI,UAAU,KAAK;AAAA,IACnC;AAEA,WAAO,EAAE,cAAc;AAAA,EACzB;AAAA,EAEQ,gCAAgC,oBAA2C;AACjF,UAAM,kBAAoC,CAAC;AAC3C,UAAM,EAAE,cAAc,IAAI;AAE1B,UAAM,YAAY,MAAM,KAAK,cAAc,QAAQ,CAAqF,EAAE,IAAI,CAAC,CAAC,UAAU,KAAK,OAAO;AAAA,MACpK;AAAA,MACA,cAAc,MAAM,OAAO,IAAK,MAAM,YAAY,MAAM,OAAQ,MAAM;AAAA,MACtE,QAAQ,MAAM;AAAA,IAChB,EAAE;AAEF,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,eAAe,UAAU;AAAA,QAAO,CAAC,MAAkE,SACvG,KAAK,eAAe,KAAK,eAAe,OAAO;AAAA,MACjD;AAEA,YAAM,gBAAgB,UAAU;AAAA,QAAO,CAAC,OAAmE,SACzG,KAAK,eAAe,MAAM,eAAe,OAAO;AAAA,MAClD;AAEA,UAAI,aAAa,eAAe,cAAc,eAAe,IAAI;AAC/D,wBAAgB,KAAK;AAAA,UACnB,IAAI,yBAAyB,KAAK,IAAI,CAAC;AAAA,UACvC,UAAU;AAAA,UACV,UAAU;AAAA,UACV,OAAO;AAAA,UACP,aAAa,GAAG,aAAa,QAAQ,QAAQ,aAAa,aAAa,QAAQ,CAAC,CAAC,sBAAsB,cAAc,QAAQ,OAAO,cAAc,aAAa,QAAQ,CAAC,CAAC;AAAA,UACzK,WAAW;AAAA,UACX,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA,YACb,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,OAAO;AAAA,cACL,gCAAgC,cAAc,QAAQ;AAAA,cACtD,kCAAkC,aAAa,QAAQ;AAAA,cACvD;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AAAA,UACD,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,WAAW,oBAAI,KAAK;AAAA,UACpB,UAAU;AAAA,YACR,SAAS,CAAC;AAAA,YACV,aAAa;AAAA,cACX,SAAS,CAAC;AAAA,cACV,YAAY;AAAA,gBACV,GAAG,aAAa,QAAQ,KAAK,aAAa,aAAa,QAAQ,CAAC,CAAC;AAAA,gBACjE,GAAG,cAAc,QAAQ,KAAK,cAAc,aAAa,QAAQ,CAAC,CAAC;AAAA,cACrE;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,yBAAyB,SAAkC;AAEjE,WAAO,CAAC;AAAA,EACV;AAAA,EAEQ,yCAAyC,mBAA0C;AACzF,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAc,gCAAgC,SAAwD;AACpG,UAAM,kBAAoC,CAAC;AAG3C,UAAM,qBAAqB,QAAQ,OAAO,OAAK,EAAE,0CAAgC;AAEjF,QAAI,mBAAmB,SAAS,GAAG;AACjC,YAAM,eAAe,mBAClB,OAAO,OAAK,EAAE,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI,CAAC,EACpE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAEjD,YAAM,oBAAoB,mBACvB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC,IAAI,mBAAmB;AAExE,UAAI,eAAe,oBAAoB,KAAK;AAC1C,wBAAgB,KAAK;AAAA,UACnB,IAAI,kBAAkB,KAAK,IAAI,CAAC;AAAA,UAChC,UAAU;AAAA,UACV,UAAU;AAAA,UACV,OAAO;AAAA,UACP,aAAa,kBAAkB,YAAY,sCAAsC,kBAAkB,QAAQ,CAAC,CAAC;AAAA,UAC7G,WAAW;AAAA,UACX,SAAS,CAAC;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA,YACb,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,OAAO;AAAA,cACL;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AAAA,UACD,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,WAAW,oBAAI,KAAK;AAAA,UACpB,UAAU;AAAA,YACR,SAAS,CAAC;AAAA,YACV,aAAa;AAAA,cACX,SAAS,CAAC;AAAA,cACV,YAAY,CAAC,kBAAkB,YAAY,yBAAyB,kBAAkB,QAAQ,CAAC,CAAC,EAAE;AAAA,YACpG;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,wBAAwB,MAA0B,iBAA6C;AAErG,UAAM,iBAAiB;AACvB,UAAM,mBAAmB,KAAK,IAAI,KAAK,gBAAgB,SAAS,EAAE;AAClE,UAAM,gBAAgB,KAAK,WAAW,KAAK;AAE3C,WAAO,KAAK,IAAI,MAAM,iBAAiB,mBAAmB,aAAa;AAAA,EACzE;AAAA,EAEQ,yBAAyB,SAA4D;AAC3F,UAAM,eAAe,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,EAAE;AAClD,UAAM,WAAW,QAAQ,OAAO,CAAC,KAAK,WAAW,MAAM,aAAa,OAAO,MAAM,GAAG,CAAC,IAAI,QAAQ;AAEjG,QAAI,YAAY,IAAK,QAAO;AAC5B,QAAI,YAAY,IAAK,QAAO;AAC5B,WAAO;AAAA,EACT;AAAA,EAEQ,yBAAyB,SAA4D;AAC3F,UAAM,eAAe,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,EAAE;AAClD,UAAM,WAAW,QAAQ,OAAO,CAAC,KAAK,WAAW,MAAM,aAAa,OAAO,MAAM,GAAG,CAAC,IAAI,QAAQ;AAEjG,QAAI,YAAY,IAAK,QAAO;AAC5B,QAAI,YAAY,IAAK,QAAO;AAC5B,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,MAA0B,iBAA6C;AAC/F,UAAM,gBAAgB,gBAAgB,SAAS,IAC3C,YAAY,gBAAgB,MAAM,yCAClC;AAEJ,WAAO,GAAG,aAAa,KAAK,KAAK,IAAI;AAAA,EACvC;AAAA,EAEQ,oBAAoB,QAAsB;AAChD,UAAM,UAAU,KAAK,qBAAqB,IAAI,MAAM,KAAK,CAAC;AAC1D,YAAQ,KAAK,oBAAI,KAAK,CAAC;AAGvB,QAAI,QAAQ,SAAS,KAAK;AACxB,cAAQ,OAAO,GAAG,QAAQ,SAAS,GAAG;AAAA,IACxC;AAEA,SAAK,qBAAqB,IAAI,QAAQ,OAAO;AAAA,EAC/C;AAAA,EAEQ,yBAAyB,iBAAqD;AAEpF,UAAM,wBAAwB,oBAAI,IAA4B;AAE9D,eAAW,OAAO,iBAAiB;AACjC,YAAM,MAAM,GAAG,IAAI,QAAQ,IAAI,IAAI,KAAK;AAExC,UAAI,CAAC,sBAAsB,IAAI,GAAG,KAC9B,sBAAsB,IAAI,GAAG,EAAG,aAAa,IAAI,YAAY;AAC/D,8BAAsB,IAAI,KAAK,GAAG;AAAA,MACpC;AAAA,IACF;AAGA,WAAO,MAAM,KAAK,sBAAsB,OAAO,CAAC,EAC7C,KAAK,CAAC,GAAG,MAAM;AACd,UAAI,EAAE,aAAa,EAAE,SAAU,QAAO,EAAE,WAAW,EAAE;AACrD,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B,CAAC;AAAA,EACL;AAAA,EAEQ,yBAA+B;AACrC,SAAK,OAAO,MAAM;AAAA,MAChB;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,UAAU;AAAA,QACV,YAAY;AAAA,UACV,EAAE,6CAAkC,UAAU,MAAM,OAAO,GAAG;AAAA,QAChE;AAAA,QACA,SAAS,CAAC;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,CAAC;AAAA,QACD,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,UAAU;AAAA,QACV,YAAY;AAAA,UACV,EAAE,uCAA+B,UAAU,MAAM,OAAO,GAAG;AAAA,QAC7D;AAAA,QACA,SAAS,CAAC;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,CAAC;AAAA,QACD,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,UAAU;AAAA,QACV,YAAY;AAAA,UACV,EAAE,6CAAkC,UAAU,MAAM,OAAO,KAAM,YAAY,EAAE,SAAS,MAAM,EAAE;AAAA,QAClG;AAAA,QACA,SAAS,CAAC;AAAA,UACR,MAAM;AAAA,UACN,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA,kBAAkB;AAAA,YAChB;AAAA,YACA,aAAa;AAAA,YACb,MAAM;AAAA,UACR;AAAA,QACF,CAAC;AAAA,QACD,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;;;ACnvBO,IAAM,qBAAN,MAAyB;AAAA,EACtB;AAAA,EACA,YAAyD,oBAAI,IAAI;AAAA,EAEjE,gBAAiC;AAAA,IACvC,iBAAiB;AAAA;AAAA,IACjB,WAAW;AAAA,MACT,SAAS;AAAA,MACT,SAAS,CAAC,OAAO,MAAM,MAAM,MAAM,MAAM,IAAI;AAAA,IAC/C;AAAA,IACA,SAAS,CAAC;AAAA,IACV,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,SAAS,CAAC;AAAA,MACZ;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,SAAS,CAAC;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY,SAAmC,CAAC,GAAG;AACjD,SAAK,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,OAAO;AACjD,SAAK,yBAAyB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBACJ,WACA,UAA+B,CAAC,GAChC,UAA8B,CAAC,GACP;AACxB,UAAM,YAA2B;AAAA,MAC/B,WAAW,oBAAI,KAAK;AAAA,MACpB;AAAA,MACA,MAAM,CAAC;AAAA,MACP,SAAS,CAAC;AAAA,MACV,UAAU,CAAC;AAAA,MACX;AAAA,IACF;AAEA,QAAI;AAEF,gBAAU,OAAO,MAAM,KAAK,cAAc,SAAS,WAAW,OAAO;AAGrE,gBAAU,UAAU,MAAM,KAAK,mBAAmB,SAAS,WAAW,OAAO;AAG7E,gBAAU,WAAW,CAAC;AAAA,IAExB,SAAS,OAAO;AACd,cAAQ,MAAM,gCAAgC,KAAK;AAAA,IACrD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,gBACL,WACA,UAA+B,CAAC,GACD;AAC/B,WAAO,MAAM;AACX,YAAM,YAAY,MAAM,KAAK,kBAAkB,WAAW,OAAO;AACjE,YAAM;AAEN,YAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,KAAK,OAAO,eAAe,CAAC;AAAA,IAC/E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,UACA,SACA,WACA,UAA+B,CAAC,GACJ;AAC5B,UAAM,SAAS,KAAK,OAAO,QAAQ,KAAK,OAAK,EAAE,OAAO,QAAQ;AAC9D,QAAI,CAAC,OAAQ,QAAO;AAEpB,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,mBAAmB,CAAC,MAAM,GAAG,SAAS,WAAW,OAAO;AAChF,aAAO,KAAK,CAAC,KAAK;AAAA,IACpB,SAAS,OAAO;AACd,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,QACb,MAAM;AAAA,QACN,aAAa,oBAAI,KAAK;AAAA,QACtB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,QAAwC;AACnD,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,GAAG,OAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,QAA+B;AACvC,SAAK,OAAO,QAAQ,KAAK,MAAM;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAA2B;AACtC,UAAM,QAAQ,KAAK,OAAO,QAAQ,UAAU,OAAK,EAAE,OAAO,QAAQ;AAClE,QAAI,SAAS,GAAG;AACd,WAAK,OAAO,QAAQ,OAAO,OAAO,CAAC;AACnC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,cACZ,SACA,WACA,SACoB;AACpB,UAAM,OAAkB,CAAC;AAGzB,UAAM,WAAW,UAAU,IAAI,QAAQ,IAAI,UAAU,MAAM,QAAQ;AACnE,UAAM,oBAAoB;AAAA,MACxB,OAAO,IAAI,KAAK,UAAU,MAAM,QAAQ,IAAI,QAAQ;AAAA,MACpD,KAAK,UAAU;AAAA,IACjB;AAGA,UAAM,cAAc,KAAK,cAAc,4CAAkC,OAAO;AAChF,UAAM,YAAY,YAAY,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAC5E,UAAM,eAAe,KAAK,6BAA6B,aAAa,iBAAiB;AAErF,SAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,eAAe;AAAA,MACf,QAAQ,YAAY;AAAA,MACpB,eAAe,eAAe,KAAM,YAAY,gBAAgB,eAAgB,MAAM;AAAA,MACtF,OAAO,YAAY,eAAe,OAAO,YAAY,eAAe,SAAS;AAAA,MAC7E,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AAGD,UAAM,mBAAmB,KAAK,cAAc,sDAAuC,OAAO;AAC1F,UAAM,iBAAiB,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AACtF,UAAM,eAAe,YAAY,IAAK,iBAAiB,YAAa,MAAM;AAE1E,SAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,gBAAgB,KAAK,OAAO,gBAAgB,KAAK,WAAW;AAAA,MACnE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,gBAAgB,KAAK,SAAS,gBAAgB,KAAK,YAAY;AAAA,IACzE,CAAC;AAGD,UAAM,gBAAgB,KAAK,cAAc,gDAAoC,OAAO;AACpF,UAAM,cAAc,cAAc,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAChF,UAAM,YAAY,YAAY,IAAK,cAAc,YAAa,MAAM;AAEpE,SAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO,aAAa,IAAI,SAAS,aAAa,KAAK,WAAW;AAAA,MAC9D,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,aAAa,IAAI,SAAS,aAAa,KAAK,YAAY;AAAA,IAClE,CAAC;AAGD,UAAM,iBAAiB,KAAK,cAAc,kDAAqC,OAAO;AACtF,UAAM,eAAe,eAAe,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAClF,UAAM,YAAY,iBAAiB,IAAK,eAAe,iBAAkB,MAAM;AAE/E,SAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAaA,MAAc,sBAAsB,MAAoC;AACtE,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,KAAK,WAAW,GAAG;AAErB,OAAC,SAAS,WAAW,OAAO,IAAI;AAChC,gBAAU,KAAK,OAAO;AAAA,IACxB,OAAO;AAEL,OAAC,SAAS,SAAS,WAAW,OAAO,IAAI;AAAA,IAC3C;AAEA,UAAM,aAA2B,CAAC;AAElC,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,yBAAyB,QAAQ,SAAS,WAAW,OAAO;AACpF,mBAAW,KAAK;AAAA,UACd,IAAI,OAAO;AAAA,UACX,OAAO,OAAO;AAAA,UACd,MAAM,OAAO;AAAA,UACb;AAAA,UACA,aAAa,oBAAI,KAAK;AAAA,QACxB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,mBAAW,KAAK;AAAA,UACd,IAAI,OAAO;AAAA,UACX,OAAO,OAAO;AAAA,UACd,MAAM,OAAO;AAAA,UACb,MAAM;AAAA,UACN,aAAa,oBAAI,KAAK;AAAA,UACtB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAClD,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,yBACZ,QACA,SACA,WACA,SACc;AAEd,UAAM,WAAW,GAAG,OAAO,EAAE,IAAI,KAAK,UAAU,OAAO,CAAC,IAAI,UAAU,MAAM,QAAQ,CAAC,IAAI,UAAU,IAAI,QAAQ,CAAC;AAChH,UAAM,SAAS,KAAK,UAAU,IAAI,QAAQ;AAC1C,UAAM,kBAAkB,OAAO,mBAAmB,KAAK,OAAO;AAE9D,QAAI,UAAW,KAAK,IAAI,IAAI,OAAO,UAAU,QAAQ,IAAK,iBAAiB;AACzE,aAAO,OAAO;AAAA,IAChB;AAGA,UAAM,kBAAkB,KAAK,kBAAkB,SAAS,OAAO,OAAO,OAAO;AAE7E,QAAI;AAEJ,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK;AACH,eAAO,KAAK,yBAAyB,iBAAiB,OAAO,aAAa;AAC1E;AAAA,MACF,KAAK;AACH,eAAO,KAAK,wBAAwB,iBAAiB,OAAO,aAAa;AACzE;AAAA,MACF,KAAK;AACH,eAAO,KAAK,wBAAwB,iBAAiB,OAAO,aAAa;AACzE;AAAA,MACF,KAAK;AACH,eAAO,KAAK,wBAAwB,iBAAiB,OAAO,aAAa;AACzE;AAAA,MACF,KAAK;AACH,eAAO,KAAK,0BAA0B,iBAAiB,OAAO,aAAa;AAC3E;AAAA,MACF,KAAK;AACH,eAAO,KAAK,wBAAwB,iBAAiB,OAAO,aAAa;AACzE;AAAA,MACF;AACE,eAAO;AAAA,IACX;AAGA,SAAK,UAAU,IAAI,UAAU,EAAE,MAAM,WAAW,oBAAI,KAAK,EAAE,CAAC;AAE5D,WAAO;AAAA,EACT;AAAA,EAEQ,yBAAyB,SAA6B,QAAkC;AAC9F,UAAM,cAAc,OAAO,eAAe;AAC1C,QAAI,QAAQ;AAEZ,YAAQ,aAAa;AAAA,MACnB,KAAK;AACH,gBAAQ,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAC9D;AAAA,MACF,KAAK;AACH,gBAAQ,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC,IAAI,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC5F;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,IAAI,GAAG,QAAQ,IAAI,OAAK,EAAE,aAAa,GAAG,CAAC;AACxD;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,IAAI,GAAG,QAAQ,IAAI,OAAK,EAAE,aAAa,GAAG,CAAC;AACxD;AAAA,MACF,KAAK;AACH,gBAAQ,QAAQ;AAChB;AAAA,IACJ;AAEA,WAAO;AAAA,MACL,OAAO,SAAS,KAAK,IAAI,QAAQ;AAAA,MACjC,WAAW,KAAK,YAAY,OAAO,MAAM;AAAA,MACzC,WAAW,oBAAI,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,wBAAwB,SAA6B,QAAkC;AAC7F,UAAM,YAAY,OAAO,aAAa;AAEtC,QAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,GAAG;AAC/C,aAAO,KAAK,yBAAyB,SAAS,MAAM;AAAA,IACtD;AAGA,UAAM,gBAAgB,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAE1F,UAAM,OAAO,cAAc,IAAI,aAAW;AAAA,MACxC,GAAG,OAAO;AAAA,MACV,GAAG,KAAK,mBAAmB,QAAQ,OAAO,eAAe,KAAK;AAAA,MAC9D,OAAO,OAAO,UAAU,YAAY;AAAA,IACtC,EAAE;AAEF,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM,CAAC;AAAA,QACL,MAAM;AAAA,QACN;AAAA,QACA,OAAO,OAAO,SAAS,CAAC,KAAK;AAAA,MAC/B,CAAC;AAAA,MACD,SAAS;AAAA,QACP,OAAO,OAAO;AAAA,QACd,OAAO,OAAO;AAAA,QACd,YAAY,OAAO,cAAc;AAAA,QACjC,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,yBAAyB,SAA6B,QAAkC;AAC9F,UAAM,UAAU,OAAO,QAAS,CAAC;AACjC,UAAM,UAAU,oBAAI,IAAgC;AAEpD,eAAW,UAAU,SAAS;AAC5B,YAAM,MAAM,OAAO,WAAW,OAAO,KAAK;AAC1C,UAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,gBAAQ,IAAI,KAAK,CAAC,CAAC;AAAA,MACrB;AACA,cAAQ,IAAI,GAAG,EAAG,KAAK,MAAM;AAAA,IAC/B;AAEA,UAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,YAAY,GAAG,WAAW;AAAA,MACjF;AAAA,MACA,MAAM,aACH,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC,EAC5D,IAAI,QAAM;AAAA,QACT,GAAG,EAAE;AAAA,QACL,GAAG,KAAK,mBAAmB,GAAG,OAAO,eAAe,KAAK;AAAA,MAC3D,EAAE;AAAA,MACJ,OAAO,OAAO,SAAS,SAAS,OAAO,QAAQ,UAAU,EAAE,KAAK,KAAK,gBAAgB,KAAK;AAAA,IAC5F,EAAE;AAEF,WAAO;AAAA,MACL,MAAM,OAAO,aAAa;AAAA,MAC1B,MAAM;AAAA,MACN,SAAS;AAAA,QACP,OAAO,OAAO;AAAA,QACd,OAAO,OAAO;AAAA,QACd,YAAY,OAAO,cAAc;AAAA,QACjC,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,wBAAwB,SAA6B,QAAkC;AAC7F,UAAM,UAAU,OAAO,WAAW,CAAC,YAAY,SAAS;AACxD,UAAM,UAAU,oBAAI,IAAgC;AAEpD,eAAW,UAAU,SAAS;AAC5B,YAAM,MAAM,QAAQ,IAAI,WAAS,OAAO,WAAW,KAAK,KAAK,SAAS,EAAE,KAAK,GAAG;AAChF,UAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,gBAAQ,IAAI,KAAK,CAAC,CAAC;AAAA,MACrB;AACA,cAAQ,IAAI,GAAG,EAAG,KAAK,MAAM;AAAA,IAC/B;AAEA,UAAM,UAAU;AAAA,MACd,GAAG,QAAQ,IAAI,YAAU,EAAE,KAAK,OAAO,OAAO,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC,EAAE,EAAE;AAAA,MAC/F,EAAE,KAAK,SAAS,OAAO,QAAQ;AAAA,MAC/B,EAAE,KAAK,OAAO,OAAO,MAAM;AAAA,MAC3B,EAAE,KAAK,OAAO,OAAO,UAAU;AAAA,IACjC;AAEA,UAAM,OAAO,MAAM,KAAK,QAAQ,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,YAAY,MAAM;AACtE,YAAM,aAAa,IAAI,MAAM,GAAG;AAChC,YAAM,MAAW,CAAC;AAElB,cAAQ,QAAQ,CAAC,OAAO,UAAU;AAChC,YAAI,KAAK,IAAI,WAAW,KAAK;AAAA,MAC/B,CAAC;AAED,UAAI,QAAQ,aAAa;AACzB,UAAI,MAAM,aAAa,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AACrE,UAAI,MAAM,aAAa,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC,IAAI,aAAa;AAEtF,aAAO;AAAA,IACT,CAAC;AAED,WAAO;AAAA,MACL;AAAA,MACA,MAAM,KAAK,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;AAAA;AAAA,IACzC;AAAA,EACF;AAAA,EAEQ,wBAAwB,SAA6B,QAAkC;AAC7F,UAAM,QAAQ,KAAK,yBAAyB,SAAS,MAAM,EAAE;AAE7D,WAAO;AAAA,MACL;AAAA,MACA,KAAK,OAAO,OAAO,OAAO;AAAA,MAC1B,KAAK,OAAO,OAAO,OAAO;AAAA,MAC1B,YAAY;AAAA,QACV,EAAE,OAAO,IAAI,OAAO,UAAU;AAAA,QAC9B,EAAE,OAAO,IAAI,OAAO,UAAU;AAAA,QAC9B,EAAE,OAAO,IAAI,OAAO,UAAU;AAAA,QAC9B,EAAE,OAAO,KAAK,OAAO,UAAU;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,0BAA0B,SAA6B,QAAkC;AAE/F,UAAM,cAA8D,CAAC;AAErE,eAAW,UAAU,SAAS;AAC5B,YAAM,OAAO,OAAO,UAAU,SAAS;AACvC,YAAM,YAAY,OAAO,UAAU,OAAO;AAE1C,kBAAY,KAAK;AAAA,QACf,GAAG;AAAA,QACH,GAAG;AAAA,QACH,OAAO,KAAK,mBAAmB,QAAQ,OAAO,eAAe,KAAK;AAAA,MACpE,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,MAAM,GAAG,CAAC,KAAK;AAAA,MACvD,SAAS,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AAAA,IAC3D;AAAA,EACF;AAAA,EAEQ,wBAAwB,SAA6B,QAAkC;AAC7F,UAAM,gBAAgB,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAE1F,QAAI,cAAc,SAAS,GAAG;AAC5B,aAAO,EAAE,OAAO,UAAU,QAAQ,GAAG,eAAe,EAAE;AAAA,IACxD;AAEA,UAAM,SAAS,cAAc,IAAI,OAAK,KAAK,mBAAmB,GAAG,OAAO,eAAe,KAAK,CAAC;AAC7F,UAAM,aAAa,OAAO,CAAC;AAC3B,UAAM,YAAY,OAAO,OAAO,SAAS,CAAC;AAE1C,UAAM,SAAS,YAAY;AAC3B,UAAM,gBAAgB,eAAe,IAAK,SAAS,aAAc,MAAM;AAEvE,QAAI,QAAkC;AACtC,QAAI,KAAK,IAAI,aAAa,IAAI,GAAG;AAC/B,cAAQ,gBAAgB,IAAI,OAAO;AAAA,IACrC;AAEA,WAAO;AAAA,MACL;AAAA,MACA,QAAQ,KAAK,MAAM,SAAS,GAAG,IAAI;AAAA,MACnC,eAAe,KAAK,MAAM,gBAAgB,GAAG,IAAI;AAAA,MACjD,QAAQ,OAAO,MAAM,GAAG;AAAA;AAAA,MACxB,WAAW,OAAO,IAAI,CAAC,OAAO,WAAW,EAAE,GAAG,OAAO,GAAG,MAAM,EAAE;AAAA,IAClE;AAAA,EACF;AAAA,EAEQ,cACN,SACA,MACA,SACoB;AACpB,WAAO,QAAQ,OAAO,YAAU;AAC9B,UAAI,OAAO,SAAS,KAAM,QAAO;AAEjC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,YAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,cAAI,CAAC,MAAM,SAAS,OAAO,WAAW,GAAG,CAAC,EAAG,QAAO;AAAA,QACtD,WAAW,SAAS,OAAO,WAAW,GAAG,MAAM,OAAO;AACpD,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,kBACN,SACA,OACA,SACoB;AACpB,QAAI,kBAAkB;AAGtB,QAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC7C,wBAAkB,gBAAgB,OAAO,OAAK,MAAM,QAAQ,SAAS,EAAE,IAAI,CAAC;AAAA,IAC9E;AAGA,QAAI,MAAM,WAAW;AACnB,wBAAkB,gBAAgB;AAAA,QAAO,OACvC,EAAE,aAAa,MAAM,UAAU,SAAS,EAAE,aAAa,MAAM,UAAU;AAAA,MACzE;AAAA,IACF;AAGA,UAAM,kBAAkB,EAAE,GAAG,MAAM,SAAS,GAAG,QAAQ;AACvD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,eAAe,GAAG;AAC1D,UAAI,UAAU,UAAa,UAAU,KAAM;AAE3C,wBAAkB,gBAAgB,OAAO,OAAK;AAC5C,YAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAO,MAAM,SAAS,EAAE,WAAW,GAAG,CAAC;AAAA,QACzC;AACA,eAAO,EAAE,WAAW,GAAG,MAAM;AAAA,MAC/B,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,6BACN,SACA,mBACQ;AAGR,UAAM,eAAe,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,aAAa,KAAK,CAAC;AAC3E,WAAO,eAAe;AAAA,EACxB;AAAA,EAEQ,mBAAmB,QAA0B,aAA6B;AAChF,YAAQ,aAAa;AAAA,MACnB,KAAK;AAAO,eAAO,OAAO,aAAa;AAAA,MACvC,KAAK;AAAO,eAAO,OAAO,aAAa;AAAA,MACvC,KAAK;AAAO,eAAO,OAAO,aAAa;AAAA,MACvC,KAAK;AAAO,eAAO,OAAO,aAAa;AAAA,MACvC,KAAK;AAAS,eAAO,OAAO,aAAa;AAAA,MACzC;AAAS,eAAO,OAAO,aAAa;AAAA,IACtC;AAAA,EACF;AAAA,EAEQ,YAAY,OAAe,QAAqC;AACtE,QAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAE7B,QAAI,SAAS,KAAS;AACpB,aAAO,IAAI,QAAQ,KAAS,QAAQ,CAAC,CAAC;AAAA,IACxC,WAAW,SAAS,KAAM;AACxB,aAAO,IAAI,QAAQ,KAAM,QAAQ,CAAC,CAAC;AAAA,IACrC;AAEA,WAAO,MAAM,QAAQ,CAAC;AAAA,EACxB;AAAA,EAEQ,gBAAgB,OAAuB;AAC7C,UAAM,SAAS;AAAA,MACb;AAAA,MAAW;AAAA,MAAW;AAAA,MAAW;AAAA,MACjC;AAAA,MAAW;AAAA,MAAW;AAAA,MAAW;AAAA,IACnC;AACA,WAAO,OAAO,QAAQ,OAAO,MAAM;AAAA,EACrC;AAAA,EAEQ,2BAAiC;AACvC,SAAK,OAAO,UAAU;AAAA,MACpB;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,QAAQ,EAAE;AAAA,QAC5C,OAAO;AAAA,UACL,SAAS,kCAAwB;AAAA,UACjC,WAAW,EAAE,OAAO,oBAAI,KAAK,GAAG,KAAK,oBAAI,KAAK,EAAE;AAAA,UAChD,UAAU;AAAA,QACZ;AAAA,QACA,eAAe;AAAA,UACb,WAAW;AAAA,UACX,aAAa;AAAA,UACb,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,GAAG,QAAQ,EAAE;AAAA,QAC5C,OAAO;AAAA,UACL,SAAS,oCAAyB;AAAA,UAClC,WAAW,EAAE,OAAO,oBAAI,KAAK,GAAG,KAAK,oBAAI,KAAK,EAAE;AAAA,QAClD;AAAA,QACA,eAAe;AAAA,UACb,aAAa;AAAA,UACb,OAAO,EAAE,KAAK,GAAG,KAAK,IAAI;AAAA,QAC5B;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,IAAI,QAAQ,EAAE;AAAA,QAC7C,OAAO;AAAA,UACL,SAAS,sHAAiF;AAAA,UAC1F,WAAW,EAAE,OAAO,oBAAI,KAAK,GAAG,KAAK,oBAAI,KAAK,EAAE;AAAA,QAClD;AAAA,QACA,eAAe;AAAA,UACb,SAAS,CAAC,UAAU;AAAA,UACpB,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACrqBO,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EACA,UAAqC,oBAAI,IAAI;AAAA,EAC7C,cAAkE,CAAC;AAAA,EAEnE,gBAA8B;AAAA,IACpC,SAAS;AAAA,MACP,EAAE,MAAM,OAAO,SAAS,EAAE,WAAW,KAAK,gBAAgB,KAAK,EAAE;AAAA,MACjE,EAAE,MAAM,OAAO;AAAA,MACf,EAAE,MAAM,OAAO,SAAS,EAAE,aAAa,YAAY,UAAU,KAAK,EAAE;AAAA,IACtE;AAAA,IACA,aAAa,KAAK,OAAO;AAAA;AAAA,IACzB,oBAAoB;AAAA,EACtB;AAAA,EAEA,YAAY,SAAgC,CAAC,GAAG;AAC9C,SAAK,SAAS,EAAE,GAAG,KAAK,eAAe,GAAG,OAAO;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,QACA,QACA,UAAe,CAAC,GACO;AACvB,UAAM,WAAW,KAAK,iBAAiB;AAEvC,QAAI;AACF,UAAI;AAEJ,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK;AACH,mBAAS,MAAM,KAAK,YAAY,QAAQ,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AACnF;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,cAAc,QAAQ,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AACrF;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,YAAY,QAAQ,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AACnF;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,aAAa,QAAQ,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AACpF;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,YAAY,QAAQ,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AACnF;AAAA,QACF;AACE,gBAAM,IAAI,MAAM,8BAA8B,OAAO,IAAI,EAAE;AAAA,MAC/D;AAEA,WAAK,QAAQ,IAAI,UAAU,MAAM;AACjC,aAAO;AAAA,IAET,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAAA,IAC9F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,SACA,QACA,UAAe,CAAC,GACO;AACvB,UAAM,WAAW,KAAK,iBAAiB;AAEvC,QAAI;AACF,UAAI;AAEJ,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK;AACH,mBAAS,MAAM,KAAK,mBAAmB,SAAS,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AAC3F;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,oBAAoB,SAAS,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AAC5F;AAAA,QACF;AACE,gBAAM,IAAI,MAAM,4CAA4C,OAAO,IAAI,EAAE;AAAA,MAC7E;AAEA,WAAK,QAAQ,IAAI,UAAU,MAAM;AACjC,aAAO;AAAA,IAET,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAAA,IACtG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eACJ,UACA,QACA,UAAe,CAAC,GACO;AACvB,UAAM,WAAW,KAAK,iBAAiB;AAEvC,QAAI;AACF,UAAI;AAEJ,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK;AACH,mBAAS,MAAM,KAAK,oBAAoB,UAAU,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AAC7F;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,qBAAqB,UAAU,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AAC9F;AAAA,QACF,KAAK;AACH,mBAAS,MAAM,KAAK,oBAAoB,UAAU,EAAE,GAAG,OAAO,SAAS,GAAG,QAAQ,GAAG,QAAQ;AAC7F;AAAA,QACF;AACE,gBAAM,IAAI,MAAM,6CAA6C,OAAO,IAAI,EAAE;AAAA,MAC9E;AAEA,WAAK,QAAQ,IAAI,UAAU,MAAM;AACjC,aAAO;AAAA,IAET,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAAA,IACvG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,UAAuC;AACrD,WAAO,KAAK,QAAQ,IAAI,QAAQ,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,QAAgB,IAAoB;AAC9C,UAAMC,WAAU,MAAM,KAAK,KAAK,QAAQ,OAAO,CAAC,EAC7C,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;AAE/D,WAAOA,SAAQ,MAAM,GAAG,KAAK;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAA2B;AACtC,WAAO,KAAK,QAAQ,OAAO,QAAQ;AAAA,EACrC;AAAA,EAEA,MAAc,YAAY,QAAyB,SAA2B,UAAyC;AACrH,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,aAAa,QAAQ,cAAc;AAEzC,QAAI,aAAa;AAGjB,QAAI,QAAQ,gBAAgB;AAC1B,oBAAc,CAAC,eAAe,SAAS,UAAU,SAAS,WAAW,EAAE,KAAK,SAAS,IAAI;AAAA,IAC3F;AAGA,eAAW,UAAU,OAAO,SAAS;AACnC,YAAM,MAAM;AAAA,QACV,OAAO,KAAK,SAAS;AAAA,QACrB,OAAO,MAAM,SAAS;AAAA,SACrB,OAAO,UAAU,GAAG,SAAS;AAAA,QAC9B,OAAO,SAAS;AAAA,QAChB,OAAO,YAAY,KAAK,UAAU,OAAO,SAAS,IAAI;AAAA,MACxD;AAEA,oBAAc,IAAI,IAAI,UAAQ,IAAI,IAAI,GAAG,EAAE,KAAK,SAAS,IAAI;AAAA,IAC/D;AAEA,UAAM,WAAW,UAAU,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAClD,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,YAAY,MAAM;AAGrD,QAAI,WAAW,KAAK,OAAO,aAAa;AACtC,YAAM,IAAI,MAAM,aAAa,QAAQ,oBAAoB,KAAK,OAAO,WAAW,EAAE;AAAA,IACpF;AAGA,YAAQ,IAAI,iBAAiB,QAAQ,WAAW,QAAQ,QAAQ;AAEhE,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,QAAyB,SAA6B,UAAyC;AAEzH,UAAM,gBAAgB;AAAA,MACpB,MAAM;AAAA,MACN,MAAM,OAAO,QAAQ,IAAI,aAAW;AAAA,QAClC,eAAe,OAAO;AAAA,QACtB,SAAS,OAAO;AAAA,QAChB,UAAU,OAAO,UAAU;AAAA,QAC3B,SAAS,OAAO,SAAS;AAAA,MAC3B,EAAE;AAAA,IACJ;AAEA,UAAM,WAAW,UAAU,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAClD,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW;AAEjB,YAAQ,IAAI,0BAA0B,QAAQ,EAAE;AAEhD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,QAAyB,SAA2B,UAAyC;AAErH,UAAM,WAAW,QAAQ,YAAY;AACrC,UAAM,cAAc,QAAQ,eAAe;AAE3C,YAAQ,IAAI,wCAAwC,QAAQ,kBAAkB,WAAW,EAAE;AAE3F,UAAM,WAAW,UAAU,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAClD,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW;AAEjB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,aAAa,QAAyB,SAAc,UAAyC;AACzG,UAAM,cAAc,KAAK,UAAU,QAAQ,MAAM,CAAC;AAClD,UAAM,WAAW,UAAU,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAClD,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,aAAa,MAAM;AAEtD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,QAAyB,SAAc,UAAyC;AAExG,QAAI,aAAa;AACjB,kBAAc;AACd,kBAAc,SAAS,OAAO,EAAE;AAAA;AAChC,kBAAc,WAAW,OAAO,IAAI;AAAA;AACpC,kBAAc,kBAAkB,OAAO,YAAY,YAAY,CAAC;AAAA;AAChE,kBAAc;AAEd,eAAW,UAAU,OAAO,SAAS;AACnC,oBAAc;AACd,oBAAc,eAAe,OAAO,IAAI;AAAA;AACxC,oBAAc,gBAAgB,OAAO,KAAK;AAAA;AAC1C,oBAAc,iBAAiB,OAAO,UAAU,CAAC;AAAA;AACjD,oBAAc,gBAAgB,OAAO,SAAS,QAAQ;AAAA;AACtD,oBAAc;AAAA,IAChB;AAEA,kBAAc;AACd,kBAAc;AAEd,UAAM,WAAW,UAAU,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAClD,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,YAAY,MAAM;AAErD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,mBAAmB,SAA6B,SAA2B,UAAyC;AAChI,UAAM,YAAY,QAAQ,aAAa;AACvC,QAAI,aAAa;AAGjB,QAAI,QAAQ,gBAAgB;AAC1B,oBAAc;AAAA,QACZ;AAAA,QAAa;AAAA,QAAQ;AAAA,QAAY;AAAA,QAAS;AAAA,QAAO;AAAA,QAAW;AAAA,QAAO;AAAA,QAAO;AAAA,MAC5E,EAAE,KAAK,SAAS,IAAI;AAAA,IACtB;AAGA,eAAW,UAAU,SAAS;AAC5B,YAAM,MAAM;AAAA,QACV,OAAO,UAAU,YAAY;AAAA,QAC7B,OAAO,KAAK,SAAS;AAAA,QACrB,OAAO;AAAA,QACP,OAAO,aAAa,MAAM,SAAS;AAAA,QACnC,OAAO,aAAa,IAAI,SAAS;AAAA,QACjC,OAAO,aAAa,IAAI,SAAS;AAAA,QACjC,OAAO,aAAa,IAAI,SAAS;AAAA,QACjC,OAAO,aAAa,IAAI,SAAS;AAAA,QACjC,KAAK,UAAU,OAAO,UAAU;AAAA,MAClC;AAEA,oBAAc,IAAI,IAAI,UAAQ,IAAI,IAAI,GAAG,EAAE,KAAK,SAAS,IAAI;AAAA,IAC/D;AAEA,UAAM,WAAW,WAAW,KAAK,IAAI,CAAC;AACtC,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,YAAY,MAAM;AAErD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,SAA6B,SAAc,UAAyC;AACpH,UAAM,cAAc,KAAK,UAAU;AAAA,MACjC,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,MACnC,OAAO,QAAQ;AAAA,MACf;AAAA,IACF,GAAG,MAAM,CAAC;AAEV,UAAM,WAAW,WAAW,KAAK,IAAI,CAAC;AACtC,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,aAAa,MAAM;AAEtD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,UAAyB,SAA2B,UAAyC;AAC7H,UAAM,YAAY,QAAQ,aAAa;AACvC,QAAI,aAAa;AAGjB,QAAI,QAAQ,gBAAgB;AAC1B,oBAAc;AAAA,QACZ;AAAA,QAAM;AAAA,QAAQ;AAAA,QAAS;AAAA,QAAe;AAAA,QAAY;AAAA,QAAU;AAAA,QAAS;AAAA,QAAc;AAAA,QAAe;AAAA,MACpG,EAAE,KAAK,SAAS,IAAI;AAAA,IACtB;AAGA,eAAW,WAAW,UAAU;AAC9B,YAAM,MAAM;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,OAAO,SAAS;AAAA,QACxB,QAAQ,MAAM,SAAS;AAAA,QACvB,QAAQ,WAAW,SAAS;AAAA,QAC5B,QAAQ,WAAW,YAAY;AAAA,SAC9B,QAAQ,mBAAmB,CAAC,GAAG,KAAK,IAAI;AAAA,MAC3C;AAEA,oBAAc,IAAI,IAAI,UAAQ,IAAI,IAAI,GAAG,EAAE,KAAK,SAAS,IAAI;AAAA,IAC/D;AAEA,UAAM,WAAW,YAAY,KAAK,IAAI,CAAC;AACvC,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,YAAY,MAAM;AAErD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,qBAAqB,UAAyB,SAAc,UAAyC;AACjH,UAAM,cAAc,KAAK,UAAU;AAAA,MACjC,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,MACnC,OAAO,SAAS;AAAA,MAChB;AAAA,IACF,GAAG,MAAM,CAAC;AAEV,UAAM,WAAW,YAAY,KAAK,IAAI,CAAC;AACvC,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW,OAAO,WAAW,aAAa,MAAM;AAEtD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,UAAyB,SAA2B,UAAyC;AAE7H,YAAQ,IAAI,gCAAgC,SAAS,MAAM,WAAW;AAEtE,UAAM,WAAW,YAAY,KAAK,IAAI,CAAC;AACvC,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,WAAW;AAEjB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,oBAAI,KAAK;AAAA,MACpB,aAAa,gBAAgB,QAAQ;AAAA,MACrC,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,IACtD;AAAA,EACF;AAAA,EAEQ,mBAA2B;AACjC,WAAO,UAAU,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAAA,EACxE;AACF;","names":["MetricType","import_events","exports"]}