em-llmepisodic-memorylong-contextcognitive-architectureskv-cachellm-inferencetutorial

EM-LLM Guide: Human-Inspired Episodic Memory AI

EM-LLM Architecture: Human-Inspired Episodic Memory, Surprise-Driven Event Segmentation, and Hierarchical Retrieval

The Infinite-Context Dilemma: The Attention Memory Wall and Fragmented Recall

In modern enterprise applications, continuous software engineering copilots, and real-time deep market research platforms powered by AiFetchly, large language models are increasingly expected to process multi-million-token information streams. Whether ingesting entire legal repositories, historical codebases, or streaming document feeds gathered through automated data fetching, autonomous AI systems require long-range contextual awareness spanning hours, days, or weeks of continuous interaction.

However, existing architectures encounter a profound physical and algorithmic bottleneck when pushed beyond standard sequence limits: the quadratic attention wall and attention dilution.

The Scaling Dilemma in Long-Context LLMs: 1. Full Quadratic Self-Attention (Standard Transformers): Context: 5,000,000 Tokens (5M) Attention Complexity: O(N^2) -> (5 x 10^6)^2 = 2.5 x 10^13 pairwise attention operations per layer! KV-Cache Memory: ~640 GB of GPU High-Bandwidth Memory (HBM) for an 8B model! Result: Immediate Out-Of-Memory (OOM) crash or prohibitive multi-second latency per token. 2. Fixed Sliding Windows / Streaming Attention (e.g., StreamingLLM): Retains only recent W tokens (e.g., 4,096 tokens) + Initial Sink Tokens. Result: Constant memory footprint, but catastrophic amnesia! All historical facts, entities, and past events outside the local window are permanently lost. 3. Standard Dense Chunk-Based RAG: Slices text into arbitrary 512-token chunks and embeds via external dense vector encoders. Result: Ignores conversational narrative coherence, breaks thoughts mid-sentence, and suffers severe retrieval blindspots on multi-hop narrative reasoning.

1. The Anatomy of Memory Failures in Modern Transformers

When foundation models attempt to ingest long contexts, they face two distinct failure modes:

  • Computational and Hardware Saturation: Standard autoregressive transformers store past Key and Value activation vectors for every processed token. For a model with sequence length $N$, layer count $L$, and hidden dimension $D$, the KV-cache memory requirement scales linearly as $\mathcal{O}(N \cdot L \cdot D)$. At $5\text{ million tokens}$, maintaining uncompressed FP16 activations requires hundreds of gigabytes of GPU VRAM per concurrent stream.
  • Attention Dilution (“Lost in the Middle”): As softmax attention normalization divides probability mass across millions of past token keys, the signal-to-noise ratio degrades. Irrelevant tokens dilute the attention distribution, causing models to hallucinate or completely fail needle-in-a-haystack retrieval tasks despite large advertised context windows.

2. Cognitive Disconnect: How Humans Outperform Transformers in Long-Term Recall

The human brain does not maintain an unbounded, uncompressed sensory buffer of every sensory perception experienced over a lifetime. Instead, cognitive neuroscience reveals that human memory is structured around two complementary mechanisms:

  1. Working Memory (WM): A transient, highly active buffer with strictly bounded capacity ($7 \pm 2$ chunks) maintaining immediate focus and syntactic coherence.
  2. Episodic Memory (EM): A structured long-term repository where continuous life streams are segmented into discrete, narrative episodes or events based on cognitive shifts and surprise. When recalling past information, the brain leverages both semantic associative similarity and temporal contiguity (recalling adjacent moments within an episodic narrative).

To bridge this fundamental architectural divide, researchers introduced EM-LLM: Human-inspired Episodic Memory for Infinite Context LLMs (published at ICLR 2025). EM-LLM translates biological episodic memory into a mathematically principled, plug-and-play architecture that scales open-weights models (such as LLaMA-3 and Mistral) to over 5 million tokens without requiring any parameter updates or fine-tuning.

The EM-LLM Architecture: Surprise-Driven Event Segmentation and Memory Trees

EM-LLM structures language model memory into a multi-tiered hierarchy consisting of a sliding Working Memory buffer, a dynamic Surprise-Driven Event Boundary Detector, and an Episodic Memory Tree governed by two-factor retrieval.

The EM-LLM Cognitive Processing Pipeline: Streaming Token Stream: [ x_1, x_2, ..., x_t, x_{t+1}, ... ] │ ▼ ┌───────────────────────────────────────────────────────────────────────────┐ │ 1. Working Memory (Active KV Cache Window, Size W = 2,048 tokens) │ │ - Computes full causal self-attention for immediate conversational state│ │ - Evaluates token-level predictive probability P(x_t | Context) │ └─────────────────────────────────────┬─────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────────────┐ │ 2. Surprise-Driven Event Boundary Detector │ │ - Computes Bayesian Surprisal: S(x_t) = -log P(x_t | Context) │ │ - Event Trigger: If S(x_t) > threshold θ_event │ │ ==> Narrative boundary detected! Commits preceding tokens as Episode │ └─────────────────────────────────────┬─────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────────────┐ │ 3. Long-Term Episodic Memory (Hierarchical Event Tree) │ │ - Each Event E_k stores its Key-Value states + Representative Embedding│ │ - Two-Factor Retrieval: Score(E_k) = α · Sim(q, E_k) + β · Contig(E_k) │ │ - Dynamically swaps relevant episodes back into active attention context│ └───────────────────────────────────────────────────────────────────────────┘

1. Bayesian Surprisal as an Event Boundary Trigger

In cognitive neuroscience, the Event Segmentation Theory (EST) posits that humans perceive an event boundary whenever prediction error spikes—that is, when the environment changes unexpectedly.

EM-LLM models this directly using the language model’s own next-token predictive distribution. For each token $x_t$ evaluated in the working memory window, its token-level surprisal $S(x_t)$ is defined as its negative log-likelihood: $$S(x_t) = -\log P(x_t \mid x_{<t})$$

When the model encounters an abrupt thematic shift, a change in narrative perspective, or a new document section, its uncertainty spikes. When surprisal exceeds a dynamic statistical threshold $\theta_{\text{event}}$: $$S(x_t) > \theta_{\text{event}} = \mu_S + \gamma \cdot \sigma_S$$ (where $\mu_S$ and $\sigma_S$ represent the running mean and standard deviation of surprisal across the recent context, and $\gamma$ is a sensitivity multiplier), EM-LLM triggers an event boundary.

The tokens between the previous boundary and the current trigger are consolidated into a discrete Episodic Event $E_k$. Unlike arbitrary fixed-chunk boundaries (e.g., splitting text blindly at 512 tokens), surprise-driven segmentation preserves self-contained semantic units and narrative coherence.

2. Episodic Memory Consolidation and Event Tree

Once an event $E_k = &#123;x_{t_a}, \dots, x_{t_b}&#125;$ is demarcated:

  1. Key-Value Cache Offloading: The full Key and Value activation tensors for $E_k$ are moved from active GPU registers into a hierarchical episodic memory store (such as host RAM or pinned memory).
  2. Event Representative Embedding: EM-LLM constructs a representative semantic vector $\mathbf{e}_k$ for the episode by pooling the top-layer hidden representations of key tokens within the event: $$\mathbf{e}k = \frac{1}{|E_k|} \sum{t \in E_k} \mathbf{h}_t^{(L)}$$
  3. Hierarchical Tree Indexing: Events are structured into a multi-scale memory tree, allowing coarse-to-fine traversal across long interaction histories.

3. Dual-Factor Retrieval: Semantic Similarity and Temporal Contiguity

When the language model encounters a query or requires historical background during text generation, it queries the Episodic Memory store. Crucially, human episodic recall does not rely solely on semantic similarity; it also exhibits strong temporal contiguity—retrieving one memory naturally primes memories that occurred immediately before and after it.

EM-LLM operationalizes this through a dual-factor scoring function: $$\text{Score}(E_k \mid q) = \alpha \cdot \text{Similarity}(q, E_k) + \beta \cdot \text{Contiguity}(E_k, E_{\text{last}})$$

where:

  • $\text{Similarity}(q, E_k) = \frac{\mathbf{q} \cdot \mathbf{e}_k}{|\mathbf{q}| |\mathbf{e}_k|}$ measures the cosine semantic alignment between the active query representation $\mathbf{q}$ and the episode embedding $\mathbf{e}_k$.
  • $\text{Contiguity}(E_k, E_{\text{last}}) = \exp\left(-\frac{|k - k_{\text{last}}|}{\tau}\right)$ prioritizes episodes that are temporally contiguous to recently retrieved events $k_{\text{last}}$.
  • $\alpha$ and $\beta$ balance semantic precision against narrative flow.

Selected top-ranking events are dynamically loaded back into the model’s active attention window, providing lossless, targeted context while keeping active GPU memory usage constant.

Deep Dive: Mathematical Formulation and Attention Routing

To understand how EM-LLM maintains mathematical consistency without altering the model’s underlying weights, we examine the formal mechanics of its hybrid attention operator.

Active Attention with Dynamic Episodic Injection

Let the current token sequence be $X_{\text{stream}}$. At any generation step $t$, the active attention context $\mathcal{C}_t$ consists of two distinct components: $$\mathcal{C}_t = \mathcal{W}t \cup \mathcal{E}{\text{retrieved}}^{(t)}$$ where:

  • $\mathcal{W}t = &#123;x{t-W+1}, \dots, x_t&#125;$ is the active sliding Working Memory window of length $W$ (e.g., $W = 2,048$ tokens).
  • $\mathcal{E}{\text{retrieved}}^{(t)} = \bigcup{j=1}^M E_{(j)}$ represents the set of $M$ retrieved long-term episodic events.

The total number of active tokens in GPU VRAM is strictly bounded by: $$N_{\text{active}} = W + M \cdot |E_{\text{avg}}| \ll N_{\text{total}}$$ For a 5,000,000-token document, if $W = 2,048$ and $M = 4$ episodes of average length 512 are retrieved, the model computes attention over only $4,096$ tokens—slashing compute complexity from $\mathcal{O}((5\times 10^6)^2)$ to $\mathcal{O}(4096^2)$, an operational reduction of over $1,400,000\times$ in attention FLOPs!

The EM-LLM Hybrid Attention Matrix: Retrieved Episode E_1 Retrieved Episode E_2 Working Memory (W) ┌───────────────────────┬───────────────────────┬────────────────────┐ Retrieved E_1 │ Intra-Event Causal │ -inf (Cross-Event │ -inf (Past cannot │ Tokens │ Attention │ Masked) │ attend to future)│ ├───────────────────────┼───────────────────────┼────────────────────┤ Retrieved E_2 │ -inf (Cross-Event │ Intra-Event Causal │ -inf (Past cannot │ Tokens │ Masked) │ Attention │ attend to future)│ ├───────────────────────┼───────────────────────┼────────────────────┤ Working Memory │ Cross-Attention │ Cross-Attention │ Full Dense Causal │ Query Tokens │ to Episode E_1 Keys │ to Episode E_2 Keys │ Attention Matrix │ └───────────────────────┴───────────────────────┴────────────────────┘

Positional Encoding and Rotary Invariance

A critical challenge in long-context retrieval is ensuring that Rotary Position Embeddings (RoPE) remain coherent when non-adjacent episodic chunks are injected into the working memory window.

EM-LLM addresses this through Relative Chunk-Aware Positional Offsets:

  • Tokens within the working memory window preserve their native relative positions $[0, 1, \dots, W-1]$.
  • Retrieved episodes utilize their original relative intra-chunk positions, while an episodic offset distance $\Delta_{\text{episode}}$ maps their Key tensors into an invariant past receptive field: $$\mathbf{K}{j}^{(\text{rot})} = \mathcal{R}{\Theta}(p_j + \Delta_k) \cdot \mathbf{K}_j$$ This ensures the model recognizes that retrieved episodes represent distant historical facts without causing positional out-of-distribution artifacts.

End-to-End Implementation: Surprise-Driven Episodic Memory in Python

To demonstrate the core mechanisms of EM-LLM, we construct a complete, runnable Python reference implementation. This module implements the Surprise-Driven Event Boundary Detector, Episodic Event Storage, and Two-Factor Similarity-Contiguity Retrieval.

""" em_llm_memory_engine.py Reference Implementation: Human-Inspired Episodic Memory for Infinite Context LLMs Based on EM-LLM (ICLR 2025). """ from dataclasses import dataclass, field from typing import List, Dict, Optional, Tuple import torch import torch.nn.functional as F import numpy as np import math @dataclass class EpisodicEvent: event_id: int token_ids: List[int] embedding: torch.Tensor # Mean hidden state representation (d_model) kv_cache_keys: torch.Tensor # Cached Key activations [Layers, Num_Heads, Seq_Len, Head_Dim] kv_cache_values: torch.Tensor # Cached Value activations [Layers, Num_Heads, Seq_Len, Head_Dim] start_pos: int end_pos: int mean_surprisal: float class SurpriseEventDetector: """ Detects episodic event boundaries using Bayesian surprisal: S(x_t) = -log P(x_t | Context). Triggers boundary when surprisal exceeds moving dynamic threshold. """ def __init__(self, sensitivity: float = 1.2, min_event_len: int = 128, max_event_len: int = 1024): self.sensitivity = sensitivity self.min_event_len = min_event_len self.max_event_len = max_event_len self.surprisal_history: List[float] = [] def compute_surprisal(self, logits: torch.Tensor, target_token_id: int) -> float: """ Computes -log P(target | logits) in natural log nats. """ log_probs = F.log_softmax(logits, dim=-1) token_log_prob = log_probs[0, target_token_id].item() surprisal = -token_log_prob self.surprisal_history.append(surprisal) return surprisal def is_boundary(self, current_event_len: int, current_surprisal: float) -> bool: # Enforce minimum event length to avoid micro-fragmentation if current_event_len < self.min_event_len: return False # Enforce maximum event length to avoid unbounded chunks if current_event_len >= self.max_event_len: return True # Dynamic threshold based on recent surprisal history recent_window = self.surprisal_history[-256:] mu = np.mean(recent_window) sigma = np.std(recent_window) + 1e-6 threshold = mu + self.sensitivity * sigma return current_surprisal > threshold class EpisodicMemoryManager: """ Manages long-term Episodic Memory storage and Dual-Factor Retrieval combining Semantic Similarity and Temporal Contiguity. """ def __init__(self, d_model: int = 512, alpha: float = 0.7, beta: float = 0.3, tau: float = 3.0): self.d_model = d_model self.alpha = alpha # Similarity weight self.beta = beta # Contiguity weight self.tau = tau # Contiguity decay temperature self.episodes: List[EpisodicEvent] = [] self.last_retrieved_idx: Optional[int] = None def store_event(self, tokens: List[int], keys: torch.Tensor, values: torch.Tensor, hidden_states: torch.Tensor, start_pos: int, end_pos: int, mean_surprisal: float) -> EpisodicEvent: """ Consolidates tokens into an EpisodicEvent and adds to long-term memory. """ # Average pooling over hidden states to generate event representation event_emb = hidden_states.mean(dim=1).squeeze(0) # [d_model] event_emb = F.normalize(event_emb, p=2, dim=0) event = EpisodicEvent( event_id=len(self.episodes), token_ids=tokens, embedding=event_emb, kv_cache_keys=keys, kv_cache_values=values, start_pos=start_pos, end_pos=end_pos, mean_surprisal=mean_surprisal ) self.episodes.append(event) return event def retrieve_events(self, query_emb: torch.Tensor, top_k: int = 2) -> List[EpisodicEvent]: """ Executes dual-factor scoring across all stored episodes: Score(E_k) = alpha * CosSim(q, E_k) + beta * exp(-|k - k_last| / tau) """ if not self.episodes: return [] query_norm = F.normalize(query_emb.squeeze(), p=2, dim=0) scores = [] for idx, event in enumerate(self.episodes): # 1. Semantic Similarity sim = torch.dot(query_norm, event.embedding).item() # 2. Temporal Contiguity if self.last_retrieved_idx is not None: distance = abs(idx - self.last_retrieved_idx) contig = math.exp(-distance / self.tau) else: contig = 0.0 total_score = self.alpha * sim + self.beta * contig scores.append((total_score, idx, event)) # Sort by total score descending scores.sort(key=lambda x: x[0], reverse=True) top_events = [item[2] for item in scores[:top_k]] if top_events: self.last_retrieved_idx = top_events[0].event_id return top_events # Execution Simulation if __name__ == "__main__": torch.manual_seed(42) d_model = 256 num_heads = 4 head_dim = 64 num_layers = 2 detector = SurpriseEventDetector(sensitivity=1.1, min_event_len=4, max_event_len=16) memory = EpisodicMemoryManager(d_model=d_model, alpha=0.7, beta=0.3) print("=== Initialized EM-LLM Cognitive Memory System ===") # Simulate a streaming token sequence with artificial narrative shifts simulated_tokens = list(range(100)) current_tokens = [] current_surprisals = [] start_pos = 0 for step, token_id in enumerate(simulated_tokens): current_tokens.append(token_id) # Simulate logit prediction: inject high surprise at tokens 20, 50, 75 (topic shifts) logits = torch.randn(1, 1000) if token_id in [20, 50, 75]: logits[0, token_id] = -5.0 # High unexpectedness! else: logits[0, token_id] = 4.0 # High confidence surprisal = detector.compute_surprisal(logits, token_id) current_surprisals.append(surprisal) # Check for event boundary trigger if detector.is_boundary(len(current_tokens), surprisal): # Simulated dummy KV tensors and hidden activations seq_len = len(current_tokens) keys = torch.randn(num_layers, num_heads, seq_len, head_dim) values = torch.randn(num_layers, num_heads, seq_len, head_dim) hidden_states = torch.randn(1, seq_len, d_model) mean_s = float(np.mean(current_surprisals)) event = memory.store_event( tokens=current_tokens, keys=keys, values=values, hidden_states=hidden_states, start_pos=start_pos, end_pos=step, mean_surprisal=mean_s ) print(f"-> Formed Episode {event.event_id}: Tokens [{event.start_pos}:{event.end_pos}] | Mean Surprisal: {mean_s:.2f}") # Reset working event accumulator start_pos = step + 1 current_tokens = [] current_surprisals = [] print(f"\nTotal Consolidated Episodic Events in Memory: {len(memory.episodes)}") # Simulate query retrieval query_vector = torch.randn(d_model) retrieved = memory.retrieve_events(query_vector, top_k=2) print("\n=== Retrieval Test ===") for r in retrieved: print(f"Retrieved Episode {r.event_id} (Tokens {r.start_pos} to {r.end_pos}) | Mean Surprisal: {r.mean_surprisal:.2f}")

Architectural Highlights of the Implementation

  1. Dynamic Event Consolidation: Tokens accumulate organically in working memory until Bayesian surprise exceeds statistical thresholds, segmenting natural boundaries rather than forcing arbitrary token cuts.
  2. Dual-Factor Retrieval Scoring: When querying historical memory, the algorithm balances cosine semantic similarity with narrative temporal contiguity ($\alpha \cdot \text{Sim} + \beta \cdot \text{Contig}$), avoiding disjointed recall.
  3. Pluggable Activation Caching: By persisting exact Key and Value activation tensors for each event, retrieved episodes can be dynamically re-injected into standard FlashAttention kernels with zero loss in representational fidelity.

For development teams building knowledge discovery workflows or enterprise AI pipelines using web scraping architectures and custom API integrations, this episodic framework enables seamless long-term context retention without infrastructure bloat.

Production Engineering and Deployment Best Practices

Deploying episodic memory systems in production requires addressing key infrastructure considerations regarding memory hierarchy, KV-cache quantization, and distributed serving.

+-----------------------------------------------------------------------------------+ | EM-LLM ENTERPRISE DEPLOYMENT CHECKLIST | +-----------------------------------------------------------------------------------+ | 1. Memory Tiering & Offload Storage: | | - Keep Working Memory (W = 2048) in GPU High-Bandwidth Memory (HBM) | | - Offload Episodic Memory Tree to Pinned Host CPU RAM via PCIe Gen5 | | - Stream past episodes via asynchronous CUDA streams (cudaStreamAsync) | | | | 2. Event Key-Value Quantization: | | - Quantize episodic Key/Value tensors using FP8 (E4M3) or INT4 per-channel | | - Slashes episodic storage by 4x to 8x with negligible retrieval degradation | | | | 3. Dynamic Threshold Tuning (gamma): | | - Code & Technical Text: Use gamma = 1.4 (higher threshold, larger episodes) | | - Conversational Dialogue: Use gamma = 1.0 (fine-grained emotional boundaries) | | | | 4. Relative RoPE Management: | | - Re-index retrieved episodic Keys with relative chunk-based rotary offsets | | - Protects model from out-of-distribution positional extrapolation failures | +-----------------------------------------------------------------------------------+

1. Multi-Tiered Memory Storage

Never store entire multi-million-token episodic libraries in active GPU High-Bandwidth Memory (HBM). Maintain only the active working memory window ($W = 2,048$ tokens) and currently retrieved episodes on GPU. Store the long-term Episodic Memory tree in pinned host system memory (CPU RAM) and stream Key-Value activations over PCIe Gen5 when triggered by the dual-factor retriever. Review our pricing and infrastructure guide to evaluate how memory offloading cuts GPU cloud expenditure.

2. KV-Cache Compression and Quantization

While active working memory tokens benefit from 16-bit precision, long-term episodic Key-Value states can be quantized to FP8 (E4M3 format) or INT4 with negligible loss in retrieval fidelity. Quantizing offloaded episodes allows a single standard server with 512 GB of host RAM to maintain tens of millions of conversational tokens ready for sub-10ms retrieval.

3. Asynchronous Prefetching

Because language generation proceeds token by token, the dual-factor retriever can predict upcoming episodic retrieval needs during generation pauses and prefetch Key-Value activations into GPU memory asynchronously, hiding PCIe transfer latency behind active decode forward passes.

Empirical Benchmark Evaluation

The ICLR 2025 paper evaluated EM-LLM across extensive long-context benchmarks, including Needle-In-A-Haystack (NIAH) up to 5,000,000 tokens, LongBench, and BAMBOO multi-hop reasoning tasks, evaluated on LLaMA-3-8B and Mistral-7B foundations.

Performance Comparison: Long-Context Recall Accuracy Needle-In-A-Haystack (NIAH) Retrieval Accuracy Across Context Lengths: Context Length: 128K Tokens 1,000,000 Tokens (1M) 5,000,000 Tokens (5M) Full Dense Attention ████████ (98%) ❌ OOM Crash! ❌ OOM Crash! StreamingLLM (Window) ██ (14%) ██ (12%) █ (8% - Amnesia) Standard Chunk RAG ██████ (74%) █████ (68%) ████ (59%) EM-LLM (ICLR 2025) ████████ (100%) ████████ (99.8%) ████████ (99.4%!) Memory Footprint at 5 Million Tokens (GB VRAM): Full Dense Attention [------------------ 640 GB+ (Crash) ------------------] EM-LLM (ICLR 2025) [-- 14.8 GB (Fits on single consumer GPU!) --]

1. Needle-In-A-Haystack (NIAH) at 5M Tokens

While standard dense foundation models crash due to Out-Of-Memory limitations beyond 128k–256k tokens, EM-LLM scales effortlessly to 5 million tokens, achieving near-perfect 99.4% retrieval accuracy. By indexing information into episodic events and retrieving them via similarity and contiguity, the model accesses needle facts without paying the quadratic attention penalty.

2. Multi-Hop Narrative Comprehension (BAMBOO & LongBench)

On narrative reasoning benchmarks requiring synthesis across multiple distant chapters, EM-LLM outperformed standard dense RAG baselines by 22.4%. Because surprise-based event boundaries preserve natural narrative arc boundaries and contiguity retrieval pulls in contextually adjacent events, EM-LLM maintains coherent understanding across complex narrative threads. Detailed integration guidelines are available in our developer documentation.

Conclusion: The Path Toward Truly Infinite Context AI

The human brain demonstrates that intelligence does not require brute-force attention over every historical sensory stimulus. True cognitive scalability arises from principled forgetting, surprise-driven event consolidation, and structured episodic recall.

EM-LLM translates these biological insights into foundational AI infrastructure. By uniting:

  • Bayesian surprise to demarcate coherent semantic events,
  • Two-factor retrieval leveraging semantic similarity and temporal contiguity, and
  • Bounded working memory preserving sub-second interactive latency,

EM-LLM enables open-source language models to break free from the constraints of context windows and scale to millions of tokens without costly fine-tuning or exorbitant hardware overheads.

As enterprise AI transitions from stateless prompt completion to long-horizon autonomous agents, episodic cognitive architectures will form the foundation of persistent artificial intelligence. Discover how AiFetchly leverages modern foundation models and intelligent data pipelines to build scalable, context-aware enterprise solutions.