use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use super::config::{CohesionDeviceType, LexicalChainType, SemanticRelationship};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LexicalCoherenceResult {
pub overall_coherence_score: f64,
pub chain_coherence_score: f64,
pub semantic_field_score: f64,
pub vocabulary_consistency: f64,
pub lexical_chains: Vec<LexicalChain>,
pub semantic_fields: HashMap<String, Vec<String>>,
pub detailed_metrics: Option<DetailedLexicalMetrics>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LexicalChain {
pub chain_id: usize,
pub words: Vec<(String, Vec<(usize, usize)>)>,
pub chain_type: LexicalChainType,
pub semantic_relationship: SemanticRelationship,
pub coherence_score: f64,
pub strength: f64,
pub average_distance: f64,
pub max_distance: f64,
pub coverage: f64,
pub density: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedLexicalMetrics {
pub lexical_diversity: Option<LexicalDiversityMetrics>,
pub pattern_statistics: Option<PatternStatistics>,
pub temporal_coherence: Option<TemporalCoherenceMetrics>,
pub chain_connectivity: Option<ChainConnectivity>,
pub cohesion_devices: Option<Vec<CohesionDevice>>,
pub advanced_analysis: Option<AdvancedChainAnalysis>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LexicalDiversityMetrics {
pub type_token_ratio: f64,
pub mattr: f64,
pub mtld: f64,
pub honore_h: f64,
pub brunet_w: f64,
pub unique_word_percentage: f64,
pub sophistication_score: f64,
pub vocabulary_range: VocabularyRange,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VocabularyRange {
pub high_frequency_percentage: f64,
pub mid_frequency_percentage: f64,
pub low_frequency_percentage: f64,
pub academic_vocabulary_percentage: f64,
pub technical_vocabulary_percentage: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PatternStatistics {
pub repetition_patterns: HashMap<String, usize>,
pub morphological_patterns: HashMap<String, Vec<String>>,
pub collocation_patterns: HashMap<String, Vec<String>>,
pub semantic_patterns: HashMap<String, usize>,
pub chain_type_distribution: HashMap<LexicalChainType, usize>,
pub relationship_distribution: HashMap<SemanticRelationship, usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalCoherenceMetrics {
pub consistency_across_segments: f64,
pub vocabulary_shifts: Vec<VocabularyShift>,
pub periodic_patterns: Vec<PeriodicPattern>,
pub temporal_clustering: f64,
pub flow_smoothness: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VocabularyShift {
pub position: usize,
pub magnitude: f64,
pub shift_type: String,
pub shift_words: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeriodicPattern {
pub pattern_id: String,
pub period: usize,
pub strength: f64,
pub pattern_words: Vec<String>,
pub positions: Vec<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionDistribution {
pub segment_distribution: HashMap<String, f64>,
pub clustering_coefficient: f64,
pub dispersion_index: f64,
pub position_variance: f64,
pub coverage_uniformity: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainConnectivity {
pub average_overlap: f64,
pub interconnectedness: f64,
pub network_density: f64,
pub connected_components: usize,
pub largest_component_size: usize,
pub average_path_length: f64,
pub clustering_coefficient: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CohesionDevice {
pub device_type: CohesionDeviceType,
pub source: String,
pub target: String,
pub source_position: (usize, usize),
pub target_position: (usize, usize),
pub distance: usize,
pub strength: f64,
pub confidence: f64,
pub context_relevance: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdvancedChainAnalysis {
pub network_analysis: Option<ChainNetworkAnalysis>,
pub information_measures: Option<InformationMeasures>,
pub cognitive_metrics: Option<CognitiveMeasures>,
pub discourse_alignment: Option<DiscourseAlignmentMetrics>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainNetworkAnalysis {
pub node_count: usize,
pub edge_count: usize,
pub density: f64,
pub average_degree: f64,
pub clustering_coefficient: f64,
pub average_path_length: f64,
pub degree_distribution: HashMap<usize, usize>,
pub central_nodes: Vec<CentralNode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CentralNode {
pub word: String,
pub degree_centrality: f64,
pub betweenness_centrality: f64,
pub closeness_centrality: f64,
pub pagerank: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InformationMeasures {
pub lexical_entropy: f64,
pub chain_entropy: f64,
pub chain_mutual_information: f64,
pub information_redundancy: f64,
pub conditional_entropy: f64,
pub information_flow: InformationFlowMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InformationFlowMetrics {
pub forward_flow: f64,
pub backward_flow: f64,
pub flow_consistency: f64,
pub information_accumulation: f64,
pub bottlenecks: Vec<InformationBottleneck>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InformationBottleneck {
pub position: usize,
pub severity: f64,
pub affected_chains: Vec<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CognitiveMeasures {
pub processing_load: f64,
pub memory_load: f64,
pub integration_effort: f64,
pub disambiguation_load: f64,
pub accessibility_score: f64,
pub working_memory_demand: f64,
pub complexity_factors: CognitiveComplexityFactors,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CognitiveComplexityFactors {
pub lexical_complexity: f64,
pub chain_complexity: f64,
pub semantic_complexity: f64,
pub structural_complexity: f64,
pub integration_complexity: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscourseAlignmentMetrics {
pub topic_alignment: f64,
pub register_consistency: f64,
pub functional_alignment: f64,
pub stylistic_coherence: f64,
pub genre_appropriateness: f64,
pub alignment_details: AlignmentDetails,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlignmentDetails {
pub topic_chains: HashMap<String, Vec<usize>>,
pub register_markers: Vec<RegisterMarker>,
pub functional_categories: HashMap<String, f64>,
pub stylistic_features: HashMap<String, f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegisterMarker {
pub marker: String,
pub category: String,
pub position: (usize, usize),
pub strength: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LexicalCoherenceStatistics {
pub total_words: usize,
pub unique_words: usize,
pub total_chains: usize,
pub average_chain_length: f64,
pub lexical_density: f64,
pub coherence_distribution: CoherenceDistribution,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoherenceDistribution {
pub score_ranges: BTreeMap<String, usize>,
pub mean_score: f64,
pub standard_deviation: f64,
pub skewness: f64,
pub kurtosis: f64,
}
impl LexicalCoherenceResult {
pub fn basic(overall_score: f64, chains: Vec<LexicalChain>) -> Self {
Self {
overall_coherence_score: overall_score,
chain_coherence_score: overall_score * 0.6,
semantic_field_score: overall_score * 0.3,
vocabulary_consistency: overall_score * 0.1,
lexical_chains: chains,
semantic_fields: HashMap::new(),
detailed_metrics: None,
}
}
pub fn has_detailed_metrics(&self) -> bool {
self.detailed_metrics.is_some()
}
pub fn get_chain_statistics(&self) -> LexicalCoherenceStatistics {
let total_chains = self.lexical_chains.len();
let total_words: usize = self
.lexical_chains
.iter()
.map(|chain| chain.words.len())
.sum();
let unique_words: usize = self
.lexical_chains
.iter()
.flat_map(|chain| chain.words.iter().map(|(word, _)| word))
.collect::<std::collections::HashSet<_>>()
.len();
let average_chain_length = if total_chains > 0 {
self.lexical_chains
.iter()
.map(|chain| chain.words.len())
.sum::<usize>() as f64
/ total_chains as f64
} else {
0.0
};
let scores: Vec<f64> = self
.lexical_chains
.iter()
.map(|chain| chain.coherence_score)
.collect();
let mean_score = if !scores.is_empty() {
scores.iter().sum::<f64>() / scores.len() as f64
} else {
0.0
};
let variance = if !scores.is_empty() {
scores
.iter()
.map(|score| (score - mean_score).powi(2))
.sum::<f64>()
/ scores.len() as f64
} else {
0.0
};
LexicalCoherenceStatistics {
total_words,
unique_words,
total_chains,
average_chain_length,
lexical_density: if total_words > 0 {
unique_words as f64 / total_words as f64
} else {
0.0
},
coherence_distribution: CoherenceDistribution {
score_ranges: BTreeMap::new(), mean_score,
standard_deviation: variance.sqrt(),
skewness: 0.0, kurtosis: 0.0, },
}
}
}
impl LexicalChain {
pub fn total_word_count(&self) -> usize {
self.words
.iter()
.map(|(_, positions)| positions.len())
.sum()
}
pub fn sentence_span(&self) -> (usize, usize) {
let all_positions: Vec<usize> = self
.words
.iter()
.flat_map(|(_, positions)| positions.iter().map(|(sent, _)| *sent))
.collect();
if all_positions.is_empty() {
(0, 0)
} else {
(
*all_positions.iter().min().expect("reduction should succeed"),
*all_positions.iter().max().expect("reduction should succeed"),
)
}
}
pub fn complexity(&self) -> f64 {
let unique_words = self.words.len() as f64;
let total_positions = self.total_word_count() as f64;
let span = self.sentence_span();
let sentence_span = (span.1 - span.0 + 1) as f64;
let diversity_factor = unique_words / total_positions.max(1.0);
let distribution_factor = total_positions / sentence_span.max(1.0);
let relationship_factor = match self.semantic_relationship {
SemanticRelationship::Synonymy => 0.9,
SemanticRelationship::Hyponymy => 0.8,
SemanticRelationship::Meronymy => 0.7,
_ => 0.5,
};
(diversity_factor + distribution_factor + relationship_factor) / 3.0
}
}
impl DetailedLexicalMetrics {
pub fn has_advanced_analysis(&self) -> bool {
self.advanced_analysis.is_some()
}
pub fn complexity_summary(&self) -> f64 {
let mut complexity = 0.0;
let mut factors = 0;
if let Some(diversity) = &self.lexical_diversity {
complexity += 1.0 - diversity.type_token_ratio; factors += 1;
}
if let Some(connectivity) = &self.chain_connectivity {
complexity += connectivity.network_density;
factors += 1;
}
if let Some(advanced) = &self.advanced_analysis {
if let Some(cognitive) = &advanced.cognitive_metrics {
complexity += cognitive.processing_load;
factors += 1;
}
}
if factors > 0 {
complexity / factors as f64
} else {
0.0
}
}
}