memscope_rs/export/binary/
variable_relationship_analyzer.rs

1//! Variable relationship analysis for binary to HTML conversion
2//!
3//! This module provides comprehensive analysis of variable relationships based on allocation data,
4//! generating D3.js compatible graph data structures for visualization.
5
6use crate::core::types::AllocationInfo;
7use crate::export::binary::error::BinaryExportError;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// Variable relationship analysis results for dashboard display
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct VariableRelationshipAnalysis {
14    /// Graph data for D3.js visualization
15    pub graph: RelationshipGraph,
16    /// Summary statistics
17    pub summary: RelationshipSummary,
18    /// Relationship patterns detected
19    pub patterns: Vec<RelationshipPattern>,
20    /// Performance optimization data
21    pub optimization: GraphOptimization,
22}
23
24/// D3.js compatible graph structure
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct RelationshipGraph {
27    /// Nodes representing variables/allocations
28    pub nodes: Vec<GraphNode>,
29    /// Edges representing relationships
30    pub links: Vec<GraphEdge>,
31    /// Graph metadata
32    pub metadata: GraphMetadata,
33}
34
35/// Node in the relationship graph
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct GraphNode {
38    /// Unique node identifier
39    pub id: String,
40    /// Variable name (if available)
41    pub name: String,
42    /// Memory address
43    pub address: usize,
44    /// Memory size in bytes
45    pub size: usize,
46    /// Type name
47    pub type_name: String,
48    /// Scope where variable exists
49    pub scope: String,
50    /// Node category for visualization
51    pub category: NodeCategory,
52    /// Ownership status
53    pub ownership: OwnershipStatus,
54    /// Lifetime information
55    pub lifetime: LifetimeInfo,
56    /// Visual properties for D3.js
57    pub visual: NodeVisual,
58    /// Relationship statistics
59    pub stats: NodeStats,
60}
61
62/// Edge in the relationship graph
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct GraphEdge {
65    /// Source node ID
66    pub source: String,
67    /// Target node ID
68    pub target: String,
69    /// Relationship type
70    pub relationship: RelationshipType,
71    /// Strength of relationship (0.0 - 1.0)
72    pub strength: f64,
73    /// Direction of relationship
74    pub direction: EdgeDirection,
75    /// Visual properties for D3.js
76    pub visual: EdgeVisual,
77    /// Metadata about the relationship
78    pub metadata: EdgeMetadata,
79}
80
81/// Graph metadata for D3.js layout
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct GraphMetadata {
84    /// Total number of nodes
85    pub node_count: usize,
86    /// Total number of edges
87    pub edge_count: usize,
88    /// Graph density (edges / max_possible_edges)
89    pub density: f64,
90    /// Clustering coefficient
91    pub clustering_coefficient: f64,
92    /// Average path length
93    pub average_path_length: f64,
94    /// Layout configuration
95    pub layout: LayoutConfig,
96    /// Performance hints
97    pub performance: PerformanceHints,
98}
99
100/// Node category for visualization grouping
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102pub enum NodeCategory {
103    /// Stack allocated variable
104    Stack,
105    /// Heap allocated variable
106    Heap,
107    /// Smart pointer (Box, Rc, Arc)
108    SmartPointer,
109    /// Reference (&T, &mut T)
110    Reference,
111    /// Raw pointer (*const T, *mut T)
112    RawPointer,
113    /// Collection (Vec, HashMap, etc.)
114    Collection,
115    /// Primitive type
116    Primitive,
117    /// Custom struct/enum
118    Custom,
119}
120
121/// Ownership status of a variable
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
123pub enum OwnershipStatus {
124    /// Owns the data
125    Owner,
126    /// Borrowed immutably
127    BorrowedImmutable,
128    /// Borrowed mutably
129    BorrowedMutable,
130    /// Shared ownership (Rc/Arc)
131    Shared,
132    /// Weak reference
133    Weak,
134    /// Unknown ownership
135    Unknown,
136}
137
138/// Lifetime information for a variable
139#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct LifetimeInfo {
141    /// Start timestamp
142    pub start: u64,
143    /// End timestamp (if deallocated)
144    pub end: Option<u64>,
145    /// Duration in milliseconds
146    pub duration_ms: Option<u64>,
147    /// Lifetime category
148    pub category: LifetimeCategory,
149    /// Is still active
150    pub is_active: bool,
151}
152
153/// Lifetime category
154#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
155pub enum LifetimeCategory {
156    /// Very short lived (<1ms)
157    Instant,
158    /// Short lived (1ms - 100ms)
159    Short,
160    /// Medium lived (100ms - 1s)
161    Medium,
162    /// Long lived (1s - 10s)
163    Long,
164    /// Very long lived (>10s)
165    Persistent,
166    /// Still active
167    Active,
168}
169
170/// Visual properties for D3.js nodes
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct NodeVisual {
173    /// X coordinate (for fixed positioning)
174    pub x: Option<f64>,
175    /// Y coordinate (for fixed positioning)
176    pub y: Option<f64>,
177    /// Node radius
178    pub radius: f64,
179    /// Color (hex string)
180    pub color: String,
181    /// Opacity (0.0 - 1.0)
182    pub opacity: f64,
183    /// CSS class for styling
184    pub css_class: String,
185    /// Whether node is fixed in position
186    pub fixed: bool,
187}
188
189/// Node statistics
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct NodeStats {
192    /// Number of incoming relationships
193    pub in_degree: usize,
194    /// Number of outgoing relationships
195    pub out_degree: usize,
196    /// Centrality score
197    pub centrality: f64,
198    /// Clustering coefficient
199    pub clustering: f64,
200    /// Page rank score
201    pub page_rank: f64,
202}
203
204/// Type of relationship between variables
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
206pub enum RelationshipType {
207    /// Ownership relationship (A owns B)
208    Ownership,
209    /// Borrowing relationship (A borrows B)
210    Borrowing,
211    /// Reference relationship (A references B)
212    Reference,
213    /// Containment relationship (A contains B)
214    Containment,
215    /// Dependency relationship (A depends on B)
216    Dependency,
217    /// Shared ownership (A and B share data)
218    SharedOwnership,
219    /// Temporal relationship (A created before B)
220    Temporal,
221    /// Memory adjacency (A and B are adjacent in memory)
222    MemoryAdjacency,
223    /// Type relationship (A and B have same type)
224    TypeSimilarity,
225}
226
227/// Direction of relationship edge
228#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
229pub enum EdgeDirection {
230    /// Directed edge (A -> B)
231    Directed,
232    /// Undirected edge (A <-> B)
233    Undirected,
234    /// Bidirectional edge (A <=> B)
235    Bidirectional,
236}
237
238/// Visual properties for D3.js edges
239#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct EdgeVisual {
241    /// Line width
242    pub width: f64,
243    /// Color (hex string)
244    pub color: String,
245    /// Opacity (0.0 - 1.0)
246    pub opacity: f64,
247    /// Line style (solid, dashed, dotted)
248    pub style: String,
249    /// CSS class for styling
250    pub css_class: String,
251    /// Arrow marker (for directed edges)
252    pub marker: Option<String>,
253}
254
255/// Edge metadata
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct EdgeMetadata {
258    /// When relationship was established
259    pub created_at: u64,
260    /// When relationship ended (if applicable)
261    pub ended_at: Option<u64>,
262    /// Confidence score (0.0 - 1.0)
263    pub confidence: f64,
264    /// Source of relationship detection
265    pub source: String,
266    /// Additional properties
267    pub properties: HashMap<String, String>,
268}
269
270/// Layout configuration for D3.js
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct LayoutConfig {
273    /// Layout algorithm (force, hierarchical, circular)
274    pub algorithm: String,
275    /// Force simulation parameters
276    pub force: ForceConfig,
277    /// Hierarchical layout parameters
278    pub hierarchical: Option<HierarchicalConfig>,
279    /// Viewport dimensions
280    pub viewport: ViewportConfig,
281}
282
283/// Force simulation configuration
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct ForceConfig {
286    /// Link force strength
287    pub link_strength: f64,
288    /// Charge force strength (repulsion)
289    pub charge_strength: f64,
290    /// Center force strength
291    pub center_strength: f64,
292    /// Collision force radius
293    pub collision_radius: f64,
294    /// Alpha decay rate
295    pub alpha_decay: f64,
296    /// Velocity decay
297    pub velocity_decay: f64,
298}
299
300/// Hierarchical layout configuration
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct HierarchicalConfig {
303    /// Direction (top-down, bottom-up, left-right, right-left)
304    pub direction: String,
305    /// Level separation
306    pub level_separation: f64,
307    /// Node separation
308    pub node_separation: f64,
309    /// Tree separation
310    pub tree_separation: f64,
311}
312
313/// Viewport configuration
314#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct ViewportConfig {
316    /// Width in pixels
317    pub width: f64,
318    /// Height in pixels
319    pub height: f64,
320    /// Zoom level
321    pub zoom: f64,
322    /// Pan X offset
323    pub pan_x: f64,
324    /// Pan Y offset
325    pub pan_y: f64,
326}
327
328/// Performance hints for large graphs
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct PerformanceHints {
331    /// Use canvas rendering for large graphs
332    pub use_canvas: bool,
333    /// Enable level-of-detail rendering
334    pub use_lod: bool,
335    /// Maximum nodes to render at once
336    pub max_visible_nodes: usize,
337    /// Enable clustering for dense areas
338    pub enable_clustering: bool,
339    /// Clustering threshold
340    pub clustering_threshold: f64,
341}
342
343/// Summary statistics for relationships
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct RelationshipSummary {
346    /// Total variables analyzed
347    pub total_variables: usize,
348    /// Total relationships found
349    pub total_relationships: usize,
350    /// Relationship type distribution
351    pub relationship_distribution: HashMap<RelationshipType, usize>,
352    /// Ownership pattern distribution
353    pub ownership_distribution: HashMap<OwnershipStatus, usize>,
354    /// Average relationships per variable
355    pub average_relationships: f64,
356    /// Graph complexity score (0-100)
357    pub complexity_score: u32,
358    /// Memory efficiency insights
359    pub memory_insights: MemoryInsights,
360}
361
362/// Detected relationship patterns
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct RelationshipPattern {
365    /// Pattern name
366    pub name: String,
367    /// Pattern description
368    pub description: String,
369    /// Nodes involved in pattern
370    pub nodes: Vec<String>,
371    /// Pattern confidence (0.0 - 1.0)
372    pub confidence: f64,
373    /// Pattern category
374    pub category: PatternCategory,
375    /// Recommendations based on pattern
376    pub recommendations: Vec<String>,
377}
378
379/// Category of relationship pattern
380#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
381pub enum PatternCategory {
382    /// Ownership chain (A owns B owns C)
383    OwnershipChain,
384    /// Circular reference
385    CircularReference,
386    /// Hub pattern (one node connected to many)
387    Hub,
388    /// Cluster pattern (tightly connected group)
389    Cluster,
390    /// Tree pattern (hierarchical structure)
391    Tree,
392    /// Memory leak pattern
393    MemoryLeak,
394    /// Optimization opportunity
395    OptimizationOpportunity,
396}
397
398/// Memory insights from relationship analysis
399#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct MemoryInsights {
401    /// Total memory represented in graph
402    pub total_memory: usize,
403    /// Memory fragmentation score
404    pub fragmentation_score: f64,
405    /// Sharing efficiency (shared memory / total memory)
406    pub sharing_efficiency: f64,
407    /// Lifetime distribution
408    pub lifetime_distribution: HashMap<LifetimeCategory, usize>,
409    /// Optimization suggestions
410    pub optimizations: Vec<String>,
411}
412
413/// Graph optimization data for performance
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct GraphOptimization {
416    /// Simplified graph for overview
417    pub simplified: Option<RelationshipGraph>,
418    /// Clustering information
419    pub clusters: Vec<NodeCluster>,
420    /// Level-of-detail configurations
421    pub lod_levels: Vec<LodLevel>,
422    /// Rendering hints
423    pub rendering: RenderingHints,
424}
425
426/// Node cluster for performance optimization
427#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct NodeCluster {
429    /// Cluster ID
430    pub id: String,
431    /// Nodes in cluster
432    pub nodes: Vec<String>,
433    /// Cluster center position
434    pub center: (f64, f64),
435    /// Cluster radius
436    pub radius: f64,
437    /// Representative node
438    pub representative: String,
439    /// Cluster statistics
440    pub stats: ClusterStats,
441}
442
443/// Cluster statistics
444#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct ClusterStats {
446    /// Number of nodes in cluster
447    pub node_count: usize,
448    /// Internal edge count
449    pub internal_edges: usize,
450    /// External edge count
451    pub external_edges: usize,
452    /// Cluster density
453    pub density: f64,
454    /// Total memory in cluster
455    pub total_memory: usize,
456}
457
458/// Level-of-detail configuration
459#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct LodLevel {
461    /// Zoom level threshold
462    pub zoom_threshold: f64,
463    /// Maximum nodes to show
464    pub max_nodes: usize,
465    /// Edge simplification factor
466    pub edge_simplification: f64,
467    /// Label visibility threshold
468    pub label_threshold: f64,
469}
470
471/// Rendering hints for optimization
472#[derive(Debug, Clone, Serialize, Deserialize)]
473pub struct RenderingHints {
474    /// Use WebGL for rendering
475    pub use_webgl: bool,
476    /// Enable instanced rendering
477    pub use_instancing: bool,
478    /// Batch size for rendering
479    pub batch_size: usize,
480    /// Frame rate target
481    pub target_fps: u32,
482    /// Memory budget in MB
483    pub memory_budget: usize,
484}
485
486/// Variable relationship analyzer
487pub struct VariableRelationshipAnalyzer {
488    /// Nodes being built
489    nodes: HashMap<String, GraphNode>,
490    /// Edges being built
491    edges: Vec<GraphEdge>,
492    /// Node ID counter
493    node_counter: usize,
494    /// Relationship detector
495    relationship_detector: RelationshipDetector,
496    /// Pattern detector
497    pattern_detector: PatternDetector,
498    /// Performance optimizer
499    optimizer: GraphOptimizer,
500}
501
502/// Internal relationship detector
503#[derive(Debug, Clone)]
504struct RelationshipDetector {
505    /// Address to node mapping
506    address_map: HashMap<usize, String>,
507    /// Type to nodes mapping
508    type_map: HashMap<String, Vec<String>>,
509    /// Scope to nodes mapping
510    scope_map: HashMap<String, Vec<String>>,
511    /// Temporal ordering
512    temporal_order: Vec<(String, u64)>,
513}
514
515/// Internal pattern detector
516#[derive(Debug, Clone)]
517struct PatternDetector {
518    /// Detected patterns
519    patterns: Vec<RelationshipPattern>,
520}
521
522/// Internal graph optimizer
523#[derive(Debug, Clone)]
524struct GraphOptimizer {}
525
526impl VariableRelationshipAnalyzer {
527    /// Create a new variable relationship analyzer
528    pub fn new() -> Self {
529        Self {
530            nodes: HashMap::new(),
531            edges: Vec::new(),
532            node_counter: 0,
533            relationship_detector: RelationshipDetector::new(),
534            pattern_detector: PatternDetector::new(),
535            optimizer: GraphOptimizer::new(),
536        }
537    }
538
539    /// Analyze variable relationships from allocation data
540    pub fn analyze_allocations(
541        allocations: &[AllocationInfo],
542    ) -> Result<VariableRelationshipAnalysis, BinaryExportError> {
543        let mut analyzer = Self::new();
544
545        // Build nodes from allocations
546        for allocation in allocations {
547            analyzer.create_node_from_allocation(allocation)?;
548        }
549
550        // Detect relationships between nodes
551        analyzer.detect_relationships(allocations)?;
552
553        // Detect patterns
554        analyzer.detect_patterns()?;
555
556        // Optimize for performance
557        analyzer.optimize_graph()?;
558
559        // Generate final analysis
560        analyzer.generate_analysis()
561    }
562
563    /// Create a graph node from allocation info
564    fn create_node_from_allocation(
565        &mut self,
566        allocation: &AllocationInfo,
567    ) -> Result<(), BinaryExportError> {
568        let node_id = format!("node_{}", self.node_counter);
569        self.node_counter += 1;
570
571        let name = allocation
572            .var_name
573            .clone()
574            .unwrap_or_else(|| format!("alloc_{:x}", allocation.ptr));
575
576        let type_name = allocation
577            .type_name
578            .clone()
579            .unwrap_or_else(|| "unknown".to_string());
580
581        let scope = allocation
582            .scope_name
583            .clone()
584            .unwrap_or_else(|| "global".to_string());
585
586        let category = self.determine_node_category(&type_name, allocation);
587        let ownership = self.determine_ownership_status(allocation);
588        let lifetime = self.create_lifetime_info(allocation);
589        let visual = self.create_node_visual(&category, allocation.size);
590        let stats = NodeStats {
591            in_degree: 0,
592            out_degree: 0,
593            centrality: 0.0,
594            clustering: 0.0,
595            page_rank: 0.0,
596        };
597
598        let node = GraphNode {
599            id: node_id.clone(),
600            name,
601            address: allocation.ptr,
602            size: allocation.size,
603            type_name: type_name.clone(),
604            scope: scope.clone(),
605            category,
606            ownership,
607            lifetime,
608            visual,
609            stats,
610        };
611
612        // Update relationship detector mappings
613        self.relationship_detector
614            .address_map
615            .insert(allocation.ptr, node_id.clone());
616        self.relationship_detector
617            .type_map
618            .entry(type_name)
619            .or_default()
620            .push(node_id.clone());
621        self.relationship_detector
622            .scope_map
623            .entry(scope)
624            .or_default()
625            .push(node_id.clone());
626        self.relationship_detector
627            .temporal_order
628            .push((node_id.clone(), allocation.timestamp_alloc));
629
630        self.nodes.insert(node_id, node);
631        Ok(())
632    }
633
634    /// Determine node category from type and allocation info
635    fn determine_node_category(
636        &self,
637        type_name: &str,
638        allocation: &AllocationInfo,
639    ) -> NodeCategory {
640        if type_name.starts_with("Box<")
641            || type_name.starts_with("Rc<")
642            || type_name.starts_with("Arc<")
643        {
644            NodeCategory::SmartPointer
645        } else if type_name.starts_with("&") {
646            NodeCategory::Reference
647        } else if type_name.starts_with("*const") || type_name.starts_with("*mut") {
648            NodeCategory::RawPointer
649        } else if type_name.starts_with("Vec<")
650            || type_name.starts_with("HashMap<")
651            || type_name.starts_with("HashSet<")
652        {
653            NodeCategory::Collection
654        } else if self.is_primitive_type(type_name) {
655            NodeCategory::Primitive
656        } else if allocation.size < 1024 {
657            // Small allocations are likely stack-allocated
658            NodeCategory::Stack
659        } else {
660            // Larger allocations are likely heap-allocated
661            NodeCategory::Heap
662        }
663    }
664
665    /// Check if type is primitive
666    fn is_primitive_type(&self, type_name: &str) -> bool {
667        matches!(
668            type_name,
669            "i8" | "i16"
670                | "i32"
671                | "i64"
672                | "i128"
673                | "isize"
674                | "u8"
675                | "u16"
676                | "u32"
677                | "u64"
678                | "u128"
679                | "usize"
680                | "f32"
681                | "f64"
682                | "bool"
683                | "char"
684                | "()"
685        )
686    }
687
688    /// Determine ownership status
689    fn determine_ownership_status(&self, allocation: &AllocationInfo) -> OwnershipStatus {
690        if let Some(ref type_name) = allocation.type_name {
691            if type_name.starts_with("&mut") {
692                OwnershipStatus::BorrowedMutable
693            } else if type_name.starts_with("&") {
694                OwnershipStatus::BorrowedImmutable
695            } else if type_name.starts_with("Rc<") || type_name.starts_with("Arc<") {
696                OwnershipStatus::Shared
697            } else if type_name.contains("Weak<") {
698                OwnershipStatus::Weak
699            } else {
700                OwnershipStatus::Owner
701            }
702        } else {
703            OwnershipStatus::Unknown
704        }
705    }
706
707    /// Create lifetime information
708    fn create_lifetime_info(&self, allocation: &AllocationInfo) -> LifetimeInfo {
709        let start = allocation.timestamp_alloc;
710        let end = allocation.timestamp_dealloc;
711        let duration_ms = allocation.lifetime_ms;
712
713        let category = if let Some(duration) = duration_ms {
714            if duration < 1 {
715                LifetimeCategory::Instant
716            } else if duration < 100 {
717                LifetimeCategory::Short
718            } else if duration < 1000 {
719                LifetimeCategory::Medium
720            } else if duration < 10000 {
721                LifetimeCategory::Long
722            } else {
723                LifetimeCategory::Persistent
724            }
725        } else {
726            LifetimeCategory::Active
727        };
728
729        LifetimeInfo {
730            start,
731            end,
732            duration_ms,
733            category,
734            is_active: end.is_none(),
735        }
736    }
737
738    /// Create visual properties for node
739    fn create_node_visual(&self, category: &NodeCategory, size: usize) -> NodeVisual {
740        let radius = (size as f64).log10().clamp(3.0, 20.0);
741
742        let color = match category {
743            NodeCategory::Stack => "#4CAF50".to_string(), // Green
744            NodeCategory::Heap => "#2196F3".to_string(),  // Blue
745            NodeCategory::SmartPointer => "#FF9800".to_string(), // Orange
746            NodeCategory::Reference => "#9C27B0".to_string(), // Purple
747            NodeCategory::RawPointer => "#F44336".to_string(), // Red
748            NodeCategory::Collection => "#00BCD4".to_string(), // Cyan
749            NodeCategory::Primitive => "#8BC34A".to_string(), // Light Green
750            NodeCategory::Custom => "#607D8B".to_string(), // Blue Grey
751        };
752
753        let css_class = format!("node-{category:?}").to_lowercase();
754
755        NodeVisual {
756            x: None,
757            y: None,
758            radius,
759            color,
760            opacity: 0.8,
761            css_class,
762            fixed: false,
763        }
764    }
765
766    /// Detect relationships between nodes
767    fn detect_relationships(
768        &mut self,
769        _allocations: &[AllocationInfo],
770    ) -> Result<(), BinaryExportError> {
771        // Sort by temporal order for temporal relationships
772        self.relationship_detector
773            .temporal_order
774            .sort_by_key(|(_, timestamp)| *timestamp);
775
776        // Detect various types of relationships
777        self.detect_ownership_relationships()?;
778        self.detect_type_relationships()?;
779        self.detect_scope_relationships()?;
780        self.detect_memory_adjacency_relationships()?;
781        self.detect_temporal_relationships()?;
782
783        Ok(())
784    }
785
786    /// Detect ownership relationships
787    fn detect_ownership_relationships(&mut self) -> Result<(), BinaryExportError> {
788        // Look for smart pointer relationships
789        for (node_id, node) in &self.nodes {
790            if matches!(node.category, NodeCategory::SmartPointer) {
791                // Find potential owned data
792                for (other_id, other_node) in &self.nodes {
793                    if node_id != other_id && self.could_be_owned_by(node, other_node) {
794                        let edge = self.create_edge(
795                            node_id.clone(),
796                            other_id.clone(),
797                            RelationshipType::Ownership,
798                            0.8,
799                            EdgeDirection::Directed,
800                        );
801                        self.edges.push(edge);
802                    }
803                }
804            }
805        }
806        Ok(())
807    }
808
809    /// Detect type relationships
810    fn detect_type_relationships(&mut self) -> Result<(), BinaryExportError> {
811        for node_ids in self.relationship_detector.type_map.values() {
812            if node_ids.len() > 1 {
813                // Create type similarity relationships
814                for i in 0..node_ids.len() {
815                    for j in i + 1..node_ids.len() {
816                        let edge = self.create_edge(
817                            node_ids[i].clone(),
818                            node_ids[j].clone(),
819                            RelationshipType::TypeSimilarity,
820                            0.5,
821                            EdgeDirection::Undirected,
822                        );
823                        self.edges.push(edge);
824                    }
825                }
826            }
827        }
828        Ok(())
829    }
830
831    /// Detect scope relationships
832    fn detect_scope_relationships(&mut self) -> Result<(), BinaryExportError> {
833        for node_ids in self.relationship_detector.scope_map.values() {
834            if node_ids.len() > 1 {
835                // Create containment relationships within same scope
836                for i in 0..node_ids.len() {
837                    for j in i + 1..node_ids.len() {
838                        let edge = self.create_edge(
839                            node_ids[i].clone(),
840                            node_ids[j].clone(),
841                            RelationshipType::Containment,
842                            0.3,
843                            EdgeDirection::Undirected,
844                        );
845                        self.edges.push(edge);
846                    }
847                }
848            }
849        }
850        Ok(())
851    }
852
853    /// Detect memory adjacency relationships
854    fn detect_memory_adjacency_relationships(&mut self) -> Result<(), BinaryExportError> {
855        let mut addresses: Vec<(usize, String)> = self
856            .nodes
857            .iter()
858            .map(|(id, node)| (node.address, id.clone()))
859            .collect();
860        addresses.sort_by_key(|(addr, _)| *addr);
861
862        for i in 0..addresses.len().saturating_sub(1) {
863            let (addr1, id1) = &addresses[i];
864            let (addr2, id2) = &addresses[i + 1];
865
866            let node1 = &self.nodes[id1];
867            let _node2 = &self.nodes[id2];
868
869            // Check if allocations are adjacent in memory
870            if addr1 + node1.size == *addr2 {
871                let edge = self.create_edge(
872                    id1.clone(),
873                    id2.clone(),
874                    RelationshipType::MemoryAdjacency,
875                    0.6,
876                    EdgeDirection::Undirected,
877                );
878                self.edges.push(edge);
879            }
880        }
881        Ok(())
882    }
883
884    /// Detect temporal relationships
885    fn detect_temporal_relationships(&mut self) -> Result<(), BinaryExportError> {
886        for i in 0..self
887            .relationship_detector
888            .temporal_order
889            .len()
890            .saturating_sub(1)
891        {
892            let (id1, _) = &self.relationship_detector.temporal_order[i];
893            let (id2, _) = &self.relationship_detector.temporal_order[i + 1];
894
895            // Create temporal relationship for consecutive allocations
896            let edge = self.create_edge(
897                id1.clone(),
898                id2.clone(),
899                RelationshipType::Temporal,
900                0.2,
901                EdgeDirection::Directed,
902            );
903            self.edges.push(edge);
904        }
905        Ok(())
906    }
907
908    /// Check if one node could be owned by another
909    fn could_be_owned_by(&self, owner: &GraphNode, owned: &GraphNode) -> bool {
910        // Simple heuristic: smart pointers could own heap allocations
911        matches!(owner.category, NodeCategory::SmartPointer)
912            && matches!(owned.category, NodeCategory::Heap | NodeCategory::Custom)
913            && owner.scope == owned.scope
914    }
915
916    /// Create an edge between two nodes
917    fn create_edge(
918        &self,
919        source: String,
920        target: String,
921        relationship: RelationshipType,
922        strength: f64,
923        direction: EdgeDirection,
924    ) -> GraphEdge {
925        let visual = self.create_edge_visual(&relationship, strength);
926        let metadata = EdgeMetadata {
927            created_at: 0, // Would be set based on analysis context
928            ended_at: None,
929            confidence: strength,
930            source: "relationship_analyzer".to_string(),
931            properties: HashMap::new(),
932        };
933
934        GraphEdge {
935            source,
936            target,
937            relationship,
938            strength,
939            direction,
940            visual,
941            metadata,
942        }
943    }
944
945    /// Create visual properties for edge
946    fn create_edge_visual(&self, relationship: &RelationshipType, strength: f64) -> EdgeVisual {
947        let width = (strength * 3.0).max(1.0);
948
949        let color = match relationship {
950            RelationshipType::Ownership => "#FF5722".to_string(), // Deep Orange
951            RelationshipType::Borrowing => "#3F51B5".to_string(), // Indigo
952            RelationshipType::Reference => "#9C27B0".to_string(), // Purple
953            RelationshipType::Containment => "#4CAF50".to_string(), // Green
954            RelationshipType::Dependency => "#FF9800".to_string(), // Orange
955            RelationshipType::SharedOwnership => "#E91E63".to_string(), // Pink
956            RelationshipType::Temporal => "#607D8B".to_string(),  // Blue Grey
957            RelationshipType::MemoryAdjacency => "#00BCD4".to_string(), // Cyan
958            RelationshipType::TypeSimilarity => "#795548".to_string(), // Brown
959        };
960
961        let style = match relationship {
962            RelationshipType::Temporal => "dashed".to_string(),
963            RelationshipType::TypeSimilarity => "dotted".to_string(),
964            _ => "solid".to_string(),
965        };
966
967        EdgeVisual {
968            width,
969            color,
970            opacity: (strength * 0.8).max(0.3),
971            style,
972            css_class: format!("edge-{relationship:?}").to_lowercase(),
973            marker: Some("arrow".to_string()),
974        }
975    }
976
977    /// Detect patterns in the graph
978    fn detect_patterns(&mut self) -> Result<(), BinaryExportError> {
979        self.pattern_detector
980            .detect_ownership_chains(&self.nodes, &self.edges)?;
981        self.pattern_detector
982            .detect_circular_references(&self.nodes, &self.edges)?;
983        self.pattern_detector
984            .detect_hub_patterns(&self.nodes, &self.edges)?;
985        self.pattern_detector
986            .detect_memory_leaks(&self.nodes, &self.edges)?;
987        Ok(())
988    }
989
990    /// Optimize graph for performance
991    fn optimize_graph(&mut self) -> Result<(), BinaryExportError> {
992        self.optimizer
993            .create_clusters(&mut self.nodes, &self.edges)?;
994        self.optimizer
995            .create_simplified_graph(&self.nodes, &self.edges)?;
996        self.optimizer.create_lod_levels(&self.nodes, &self.edges)?;
997        Ok(())
998    }
999
1000    /// Generate the final analysis
1001    fn generate_analysis(&self) -> Result<VariableRelationshipAnalysis, BinaryExportError> {
1002        let nodes: Vec<GraphNode> = self.nodes.values().cloned().collect();
1003        let links = self.edges.clone();
1004
1005        let metadata = self.create_graph_metadata(&nodes, &links);
1006
1007        let graph = RelationshipGraph {
1008            nodes,
1009            links,
1010            metadata,
1011        };
1012
1013        let summary = self.create_relationship_summary(&graph)?;
1014        let patterns = self.pattern_detector.patterns.clone();
1015        let optimization = self.optimizer.create_optimization_data(&graph)?;
1016
1017        Ok(VariableRelationshipAnalysis {
1018            graph,
1019            summary,
1020            patterns,
1021            optimization,
1022        })
1023    }
1024
1025    /// Create graph metadata
1026    fn create_graph_metadata(&self, nodes: &[GraphNode], edges: &[GraphEdge]) -> GraphMetadata {
1027        let node_count = nodes.len();
1028        let edge_count = edges.len();
1029        let max_edges = if node_count > 1 {
1030            node_count * (node_count - 1) / 2
1031        } else {
1032            1 // Avoid division by zero
1033        };
1034        let density = if max_edges > 0 {
1035            (edge_count as f64 / max_edges as f64).min(1.0) // Cap at 1.0
1036        } else {
1037            0.0
1038        };
1039
1040        let layout = LayoutConfig {
1041            algorithm: "force".to_string(),
1042            force: ForceConfig {
1043                link_strength: 0.1,
1044                charge_strength: -300.0,
1045                center_strength: 0.1,
1046                collision_radius: 5.0,
1047                alpha_decay: 0.0228,
1048                velocity_decay: 0.4,
1049            },
1050            hierarchical: None,
1051            viewport: ViewportConfig {
1052                width: 800.0,
1053                height: 600.0,
1054                zoom: 1.0,
1055                pan_x: 0.0,
1056                pan_y: 0.0,
1057            },
1058        };
1059
1060        let performance = PerformanceHints {
1061            use_canvas: node_count > 1000,
1062            use_lod: node_count > 500,
1063            max_visible_nodes: 1000,
1064            enable_clustering: node_count > 200,
1065            clustering_threshold: 0.7,
1066        };
1067
1068        GraphMetadata {
1069            node_count,
1070            edge_count,
1071            density,
1072            clustering_coefficient: 0.0, // Would be calculated in real implementation
1073            average_path_length: 0.0,    // Would be calculated in real implementation
1074            layout,
1075            performance,
1076        }
1077    }
1078
1079    /// Create relationship summary
1080    fn create_relationship_summary(
1081        &self,
1082        graph: &RelationshipGraph,
1083    ) -> Result<RelationshipSummary, BinaryExportError> {
1084        let total_variables = graph.nodes.len();
1085        let total_relationships = graph.links.len();
1086
1087        let mut relationship_distribution = HashMap::new();
1088        let mut ownership_distribution = HashMap::new();
1089
1090        for edge in &graph.links {
1091            *relationship_distribution
1092                .entry(edge.relationship.clone())
1093                .or_insert(0) += 1;
1094        }
1095
1096        for node in &graph.nodes {
1097            *ownership_distribution
1098                .entry(node.ownership.clone())
1099                .or_insert(0) += 1;
1100        }
1101
1102        let average_relationships = if total_variables > 0 {
1103            total_relationships as f64 / total_variables as f64
1104        } else {
1105            0.0
1106        };
1107
1108        let complexity_score = self.calculate_complexity_score(graph);
1109        let memory_insights = self.create_memory_insights(graph);
1110
1111        Ok(RelationshipSummary {
1112            total_variables,
1113            total_relationships,
1114            relationship_distribution,
1115            ownership_distribution,
1116            average_relationships,
1117            complexity_score,
1118            memory_insights,
1119        })
1120    }
1121
1122    /// Calculate graph complexity score
1123    fn calculate_complexity_score(&self, graph: &RelationshipGraph) -> u32 {
1124        let base_score = (graph.metadata.density * 50.0) as u32;
1125        let edge_penalty = (graph.links.len() / 10).min(30) as u32;
1126        let node_penalty = (graph.nodes.len() / 50).min(20) as u32;
1127
1128        (base_score + edge_penalty + node_penalty).min(100)
1129    }
1130
1131    /// Create memory insights
1132    fn create_memory_insights(&self, graph: &RelationshipGraph) -> MemoryInsights {
1133        let total_memory: usize = graph.nodes.iter().map(|n| n.size).sum();
1134
1135        let mut lifetime_distribution = HashMap::new();
1136        for node in &graph.nodes {
1137            *lifetime_distribution
1138                .entry(node.lifetime.category.clone())
1139                .or_insert(0) += 1;
1140        }
1141
1142        MemoryInsights {
1143            total_memory,
1144            fragmentation_score: 0.5, // Simplified
1145            sharing_efficiency: 0.7,  // Simplified
1146            lifetime_distribution,
1147            optimizations: vec![
1148                "Consider using Rc/Arc for shared data".to_string(),
1149                "Review long-lived allocations".to_string(),
1150            ],
1151        }
1152    }
1153}
1154
1155impl RelationshipDetector {
1156    fn new() -> Self {
1157        Self {
1158            address_map: HashMap::new(),
1159            type_map: HashMap::new(),
1160            scope_map: HashMap::new(),
1161            temporal_order: Vec::new(),
1162        }
1163    }
1164}
1165
1166impl PatternDetector {
1167    fn new() -> Self {
1168        Self {
1169            patterns: Vec::new(),
1170        }
1171    }
1172
1173    fn detect_ownership_chains(
1174        &mut self,
1175        _nodes: &HashMap<String, GraphNode>,
1176        _edges: &[GraphEdge],
1177    ) -> Result<(), BinaryExportError> {
1178        // Implementation would detect chains of ownership relationships
1179        Ok(())
1180    }
1181
1182    fn detect_circular_references(
1183        &mut self,
1184        _nodes: &HashMap<String, GraphNode>,
1185        _edges: &[GraphEdge],
1186    ) -> Result<(), BinaryExportError> {
1187        // Implementation would detect circular reference patterns
1188        Ok(())
1189    }
1190
1191    fn detect_hub_patterns(
1192        &mut self,
1193        _nodes: &HashMap<String, GraphNode>,
1194        _edges: &[GraphEdge],
1195    ) -> Result<(), BinaryExportError> {
1196        // Implementation would detect hub patterns (nodes with many connections)
1197        Ok(())
1198    }
1199
1200    fn detect_memory_leaks(
1201        &mut self,
1202        nodes: &HashMap<String, GraphNode>,
1203        _edges: &[GraphEdge],
1204    ) -> Result<(), BinaryExportError> {
1205        // Detect potential memory leaks
1206        let leaked_nodes: Vec<String> = nodes
1207            .iter()
1208            .filter(|(_, node)| {
1209                !node.lifetime.is_active && node.lifetime.category == LifetimeCategory::Persistent
1210            })
1211            .map(|(id, _)| id.clone())
1212            .collect();
1213
1214        if !leaked_nodes.is_empty() {
1215            let pattern = RelationshipPattern {
1216                name: "Potential Memory Leak".to_string(),
1217                description: "Long-lived allocations that may indicate memory leaks".to_string(),
1218                nodes: leaked_nodes,
1219                confidence: 0.6,
1220                category: PatternCategory::MemoryLeak,
1221                recommendations: vec![
1222                    "Review allocation lifetimes".to_string(),
1223                    "Consider using RAII patterns".to_string(),
1224                ],
1225            };
1226            self.patterns.push(pattern);
1227        }
1228
1229        Ok(())
1230    }
1231}
1232
1233impl GraphOptimizer {
1234    fn new() -> Self {
1235        Self {}
1236    }
1237
1238    fn create_clusters(
1239        &mut self,
1240        _nodes: &mut HashMap<String, GraphNode>,
1241        _edges: &[GraphEdge],
1242    ) -> Result<(), BinaryExportError> {
1243        // Implementation would create node clusters for performance
1244        Ok(())
1245    }
1246
1247    fn create_simplified_graph(
1248        &mut self,
1249        _nodes: &HashMap<String, GraphNode>,
1250        _edges: &[GraphEdge],
1251    ) -> Result<(), BinaryExportError> {
1252        // Implementation would create simplified version of graph
1253        Ok(())
1254    }
1255
1256    fn create_lod_levels(
1257        &mut self,
1258        _nodes: &HashMap<String, GraphNode>,
1259        _edges: &[GraphEdge],
1260    ) -> Result<(), BinaryExportError> {
1261        // Implementation would create level-of-detail configurations
1262        Ok(())
1263    }
1264
1265    fn create_optimization_data(
1266        &self,
1267        _graph: &RelationshipGraph,
1268    ) -> Result<GraphOptimization, BinaryExportError> {
1269        Ok(GraphOptimization {
1270            simplified: None,
1271            clusters: Vec::new(),
1272            lod_levels: vec![
1273                LodLevel {
1274                    zoom_threshold: 0.5,
1275                    max_nodes: 100,
1276                    edge_simplification: 0.8,
1277                    label_threshold: 0.3,
1278                },
1279                LodLevel {
1280                    zoom_threshold: 1.0,
1281                    max_nodes: 500,
1282                    edge_simplification: 0.5,
1283                    label_threshold: 0.6,
1284                },
1285            ],
1286            rendering: RenderingHints {
1287                use_webgl: true,
1288                use_instancing: true,
1289                batch_size: 1000,
1290                target_fps: 60,
1291                memory_budget: 256,
1292            },
1293        })
1294    }
1295}
1296
1297impl Default for VariableRelationshipAnalyzer {
1298    fn default() -> Self {
1299        Self::new()
1300    }
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305    use super::*;
1306
1307    fn create_test_allocation(
1308        ptr: usize,
1309        size: usize,
1310        var_name: Option<&str>,
1311        type_name: Option<&str>,
1312        scope_name: Option<&str>,
1313    ) -> AllocationInfo {
1314        AllocationInfo {
1315            ptr,
1316            size,
1317            var_name: var_name.map(|s| s.to_string()),
1318            type_name: type_name.map(|s| s.to_string()),
1319            scope_name: scope_name.map(|s| s.to_string()),
1320            timestamp_alloc: 1000,
1321            timestamp_dealloc: None,
1322            thread_id: "main".to_string(),
1323            borrow_count: 0,
1324            stack_trace: None,
1325            is_leaked: false,
1326            lifetime_ms: Some(100),
1327            borrow_info: None,
1328            clone_info: None,
1329            ownership_history_available: false,
1330            smart_pointer_info: None,
1331            memory_layout: None,
1332            generic_info: None,
1333            dynamic_type_info: None,
1334            runtime_state: None,
1335            stack_allocation: None,
1336            temporary_object: None,
1337            fragmentation_analysis: None,
1338            generic_instantiation: None,
1339            type_relationships: None,
1340            type_usage: None,
1341            function_call_tracking: None,
1342            lifecycle_tracking: None,
1343            access_tracking: None,
1344            drop_chain_analysis: None,
1345        }
1346    }
1347
1348    #[test]
1349    fn test_node_category_determination() {
1350        let analyzer = VariableRelationshipAnalyzer::new();
1351        let allocation =
1352            create_test_allocation(0x1000, 1024, Some("test"), Some("Vec<u8>"), Some("main"));
1353
1354        let category = analyzer.determine_node_category("Vec<u8>", &allocation);
1355        assert_eq!(category, NodeCategory::Collection);
1356
1357        let category = analyzer.determine_node_category("Box<i32>", &allocation);
1358        assert_eq!(category, NodeCategory::SmartPointer);
1359
1360        let category = analyzer.determine_node_category("&str", &allocation);
1361        assert_eq!(category, NodeCategory::Reference);
1362
1363        let category = analyzer.determine_node_category("*mut u8", &allocation);
1364        assert_eq!(category, NodeCategory::RawPointer);
1365    }
1366
1367    #[test]
1368    fn test_ownership_status_determination() {
1369        let analyzer = VariableRelationshipAnalyzer::new();
1370
1371        let allocation =
1372            create_test_allocation(0x1000, 1024, Some("test"), Some("&mut i32"), Some("main"));
1373        let ownership = analyzer.determine_ownership_status(&allocation);
1374        assert_eq!(ownership, OwnershipStatus::BorrowedMutable);
1375
1376        let allocation =
1377            create_test_allocation(0x1000, 1024, Some("test"), Some("&i32"), Some("main"));
1378        let ownership = analyzer.determine_ownership_status(&allocation);
1379        assert_eq!(ownership, OwnershipStatus::BorrowedImmutable);
1380
1381        let allocation =
1382            create_test_allocation(0x1000, 1024, Some("test"), Some("Rc<i32>"), Some("main"));
1383        let ownership = analyzer.determine_ownership_status(&allocation);
1384        assert_eq!(ownership, OwnershipStatus::Shared);
1385    }
1386
1387    #[test]
1388    fn test_lifetime_category_determination() {
1389        let analyzer = VariableRelationshipAnalyzer::new();
1390
1391        let mut allocation =
1392            create_test_allocation(0x1000, 1024, Some("test"), Some("i32"), Some("main"));
1393        allocation.lifetime_ms = Some(0);
1394        let lifetime = analyzer.create_lifetime_info(&allocation);
1395        assert_eq!(lifetime.category, LifetimeCategory::Instant);
1396
1397        allocation.lifetime_ms = Some(50);
1398        let lifetime = analyzer.create_lifetime_info(&allocation);
1399        assert_eq!(lifetime.category, LifetimeCategory::Short);
1400
1401        allocation.lifetime_ms = Some(500);
1402        let lifetime = analyzer.create_lifetime_info(&allocation);
1403        assert_eq!(lifetime.category, LifetimeCategory::Medium);
1404
1405        allocation.lifetime_ms = None;
1406        let lifetime = analyzer.create_lifetime_info(&allocation);
1407        assert_eq!(lifetime.category, LifetimeCategory::Active);
1408    }
1409
1410    #[test]
1411    fn test_variable_relationship_analysis() {
1412        let allocations = vec![
1413            create_test_allocation(0x1000, 1024, Some("vec1"), Some("Vec<u8>"), Some("main")),
1414            create_test_allocation(0x2000, 2048, Some("box1"), Some("Box<i32>"), Some("main")),
1415            create_test_allocation(0x3000, 512, Some("ref1"), Some("&str"), Some("main")),
1416            create_test_allocation(0x4000, 256, Some("int1"), Some("i32"), Some("helper")),
1417        ];
1418
1419        let analysis = VariableRelationshipAnalyzer::analyze_allocations(&allocations)
1420            .expect("Failed to get test value");
1421
1422        assert_eq!(analysis.graph.nodes.len(), 4);
1423        assert_eq!(analysis.summary.total_variables, 4);
1424        assert!(analysis.summary.total_relationships > 0);
1425        assert!(!analysis.summary.relationship_distribution.is_empty());
1426        assert!(!analysis.summary.ownership_distribution.is_empty());
1427    }
1428
1429    #[test]
1430    fn test_primitive_type_detection() {
1431        let analyzer = VariableRelationshipAnalyzer::new();
1432
1433        assert!(analyzer.is_primitive_type("i32"));
1434        assert!(analyzer.is_primitive_type("f64"));
1435        assert!(analyzer.is_primitive_type("bool"));
1436        assert!(analyzer.is_primitive_type("char"));
1437        assert!(!analyzer.is_primitive_type("String"));
1438        assert!(!analyzer.is_primitive_type("Vec<u8>"));
1439    }
1440
1441    #[test]
1442    fn test_graph_metadata_creation() {
1443        let nodes = vec![GraphNode {
1444            id: "node1".to_string(),
1445            name: "test1".to_string(),
1446            address: 0x1000,
1447            size: 1024,
1448            type_name: "i32".to_string(),
1449            scope: "main".to_string(),
1450            category: NodeCategory::Primitive,
1451            ownership: OwnershipStatus::Owner,
1452            lifetime: LifetimeInfo {
1453                start: 1000,
1454                end: None,
1455                duration_ms: None,
1456                category: LifetimeCategory::Active,
1457                is_active: true,
1458            },
1459            visual: NodeVisual {
1460                x: None,
1461                y: None,
1462                radius: 5.0,
1463                color: "#4CAF50".to_string(),
1464                opacity: 0.8,
1465                css_class: "node-primitive".to_string(),
1466                fixed: false,
1467            },
1468            stats: NodeStats {
1469                in_degree: 0,
1470                out_degree: 1,
1471                centrality: 0.5,
1472                clustering: 0.0,
1473                page_rank: 0.25,
1474            },
1475        }];
1476
1477        let edges = vec![];
1478        let analyzer = VariableRelationshipAnalyzer::new();
1479        let metadata = analyzer.create_graph_metadata(&nodes, &edges);
1480
1481        assert_eq!(metadata.node_count, 1);
1482        assert_eq!(metadata.edge_count, 0);
1483        assert_eq!(metadata.density, 0.0);
1484        assert_eq!(metadata.layout.algorithm, "force");
1485    }
1486
1487    #[test]
1488    fn test_edge_creation() {
1489        let analyzer = VariableRelationshipAnalyzer::new();
1490
1491        let edge = analyzer.create_edge(
1492            "node1".to_string(),
1493            "node2".to_string(),
1494            RelationshipType::Ownership,
1495            0.8,
1496            EdgeDirection::Directed,
1497        );
1498
1499        assert_eq!(edge.source, "node1");
1500        assert_eq!(edge.target, "node2");
1501        assert_eq!(edge.relationship, RelationshipType::Ownership);
1502        assert_eq!(edge.strength, 0.8);
1503        assert_eq!(edge.direction, EdgeDirection::Directed);
1504    }
1505
1506    #[test]
1507    fn test_memory_insights_creation() {
1508        let graph = RelationshipGraph {
1509            nodes: vec![GraphNode {
1510                id: "node1".to_string(),
1511                name: "test1".to_string(),
1512                address: 0x1000,
1513                size: 1024,
1514                type_name: "i32".to_string(),
1515                scope: "main".to_string(),
1516                category: NodeCategory::Primitive,
1517                ownership: OwnershipStatus::Owner,
1518                lifetime: LifetimeInfo {
1519                    start: 1000,
1520                    end: None,
1521                    duration_ms: Some(100),
1522                    category: LifetimeCategory::Short,
1523                    is_active: false,
1524                },
1525                visual: NodeVisual {
1526                    x: None,
1527                    y: None,
1528                    radius: 5.0,
1529                    color: "#4CAF50".to_string(),
1530                    opacity: 0.8,
1531                    css_class: "node-primitive".to_string(),
1532                    fixed: false,
1533                },
1534                stats: NodeStats {
1535                    in_degree: 0,
1536                    out_degree: 0,
1537                    centrality: 0.0,
1538                    clustering: 0.0,
1539                    page_rank: 0.0,
1540                },
1541            }],
1542            links: vec![],
1543            metadata: GraphMetadata {
1544                node_count: 1,
1545                edge_count: 0,
1546                density: 0.0,
1547                clustering_coefficient: 0.0,
1548                average_path_length: 0.0,
1549                layout: LayoutConfig {
1550                    algorithm: "force".to_string(),
1551                    force: ForceConfig {
1552                        link_strength: 0.1,
1553                        charge_strength: -300.0,
1554                        center_strength: 0.1,
1555                        collision_radius: 5.0,
1556                        alpha_decay: 0.0228,
1557                        velocity_decay: 0.4,
1558                    },
1559                    hierarchical: None,
1560                    viewport: ViewportConfig {
1561                        width: 800.0,
1562                        height: 600.0,
1563                        zoom: 1.0,
1564                        pan_x: 0.0,
1565                        pan_y: 0.0,
1566                    },
1567                },
1568                performance: PerformanceHints {
1569                    use_canvas: false,
1570                    use_lod: false,
1571                    max_visible_nodes: 1000,
1572                    enable_clustering: false,
1573                    clustering_threshold: 0.7,
1574                },
1575            },
1576        };
1577
1578        let analyzer = VariableRelationshipAnalyzer::new();
1579        let insights = analyzer.create_memory_insights(&graph);
1580
1581        assert_eq!(insights.total_memory, 1024);
1582        assert!(!insights.lifetime_distribution.is_empty());
1583        assert!(!insights.optimizations.is_empty());
1584    }
1585}