use crate::formula::{grade_clamp, inputs, round_to, Formula, FormulaResult, TextStatisticsHelper};
use crate::language::Language;
use crate::text::TextStatistics;
pub struct Gulpease;
impl Formula for Gulpease {
fn name(&self) -> &'static str {
"gulpease"
}
fn description(&self) -> &'static str {
"Gulpease Index - Italian readability formula. Uses letter count instead of syllables."
}
fn supported_languages(&self) -> &'static [&'static str] {
&["it"]
}
fn calculate(&self, stats: &TextStatistics, language: &Language) -> FormulaResult {
let w = stats.word_count.max(1) as f64;
let score =
89.0 + (300.0 * stats.sentence_count as f64 - 10.0 * stats.letter_count as f64) / w;
FormulaResult {
formula_name: self.name().to_string(),
language_code: language.code.clone(),
score: round_to(score, 1),
grade_level: None,
interpretation: interpret_gulpease(score).to_string(),
inputs: inputs(&[
("letterCount", stats.letter_count as f64),
("wordCount", stats.word_count as f64),
("sentenceCount", stats.sentence_count as f64),
]),
}
}
}
fn interpret_gulpease(score: f64) -> &'static str {
if score >= 80.0 {
"Easy for elementary school"
} else if score >= 60.0 {
"Easy for middle school"
} else if score >= 40.0 {
"Easy for high school"
} else {
"Difficult for high school"
}
}
pub struct FogPL;
impl Formula for FogPL {
fn name(&self) -> &'static str {
"fog_pl"
}
fn description(&self) -> &'static str {
"FOG-PL - Polish adaptation of Gunning Fog Index."
}
fn supported_languages(&self) -> &'static [&'static str] {
&["pl"]
}
fn calculate(&self, stats: &TextStatistics, language: &Language) -> FormulaResult {
let w = stats.word_count.max(1) as f64;
let s = stats.sentence_count.max(1) as f64;
let hard_words_pct = (stats.polysyllable_count as f64 / w) * 100.0;
let asl = w / s;
let score = 0.4 * (asl + hard_words_pct);
FormulaResult {
formula_name: self.name().to_string(),
language_code: language.code.clone(),
score: round_to(score, 1),
grade_level: Some(grade_clamp(score, 0.0, 19.0)),
interpretation: interpret_fog_pl(score).to_string(),
inputs: inputs(&[
("asl", round_to(asl, 2)),
("hardWordsPct", round_to(hard_words_pct, 2)),
("polysyllableCount", stats.polysyllable_count as f64),
]),
}
}
}
fn interpret_fog_pl(score: f64) -> &'static str {
if score < 6.0 {
"Very Easy"
} else if score < 8.0 {
"Easy"
} else if score < 12.0 {
"Standard"
} else if score < 14.0 {
"Hard"
} else {
"Very Hard"
}
}
pub struct DaleChall;
impl Formula for DaleChall {
fn name(&self) -> &'static str {
"dale_chall"
}
fn description(&self) -> &'static str {
"Dale-Chall Readability Score - estimates difficult words via syllable heuristic (1-syllable = easy). NOTE: This is a simplified estimation, not the original 3000-word Dale list."
}
fn supported_languages(&self) -> &'static [&'static str] {
&["en-us", "en-gb"]
}
fn calculate(&self, stats: &TextStatistics, language: &Language) -> FormulaResult {
let difficult_pct = TextStatisticsHelper::estimate_difficult_percentage(stats);
let raw_score = 0.1579 * difficult_pct + 0.0496 * stats.average_words_per_sentence;
let adjusted = if difficult_pct > 5.0 {
raw_score + 3.6365
} else {
raw_score
};
FormulaResult {
formula_name: self.name().to_string(),
language_code: language.code.clone(),
score: round_to(adjusted, 1),
grade_level: None,
interpretation: interpret_dale_chall(adjusted).to_string(),
inputs: inputs(&[
("difficultWordPct", round_to(difficult_pct, 1)),
("rawScore", round_to(raw_score, 4)),
("averageWordsPerSentence", stats.average_words_per_sentence),
]),
}
}
}
fn interpret_dale_chall(score: f64) -> &'static str {
if score <= 4.9 {
"4th grade or below"
} else if score <= 5.9 {
"5th-6th grade"
} else if score <= 6.9 {
"7th-8th grade"
} else if score <= 7.9 {
"9th-10th grade"
} else if score <= 8.9 {
"11th-12th grade"
} else if score <= 9.9 {
"College"
} else {
"Graduate"
}
}
pub struct Spache;
impl Formula for Spache {
fn name(&self) -> &'static str {
"spache"
}
fn description(&self) -> &'static str {
"Spache Readability Score - for primary-grade texts (K-4). Estimates difficult words via syllable heuristic (1-syllable = easy). NOTE: This is a simplified estimation, not the original Spache word list."
}
fn supported_languages(&self) -> &'static [&'static str] {
&["en-us", "en-gb"]
}
fn calculate(&self, stats: &TextStatistics, language: &Language) -> FormulaResult {
let difficult_pct = TextStatisticsHelper::estimate_difficult_percentage(stats);
let score = 0.121 * stats.average_words_per_sentence + 0.082 * difficult_pct + 0.659;
FormulaResult {
formula_name: self.name().to_string(),
language_code: language.code.clone(),
score: round_to(score, 1),
grade_level: Some(grade_clamp(score, 0.0, 5.0)),
interpretation: interpret_spache(score).to_string(),
inputs: inputs(&[
("averageWordsPerSentence", stats.average_words_per_sentence),
("difficultWordPct", round_to(difficult_pct, 2)),
]),
}
}
}
fn interpret_spache(score: f64) -> &'static str {
if score <= 2.0 {
"1st Grade"
} else if score <= 2.5 {
"2nd Grade"
} else if score <= 3.0 {
"3rd Grade"
} else if score <= 3.5 {
"4th Grade"
} else {
"Above 4th Grade"
}
}
pub struct Osman;
impl Formula for Osman {
fn name(&self) -> &'static str {
"osman"
}
fn description(&self) -> &'static str {
"OSMAN - Arabic readability formula combining Flesch and Fog adaptations."
}
fn supported_languages(&self) -> &'static [&'static str] {
&["ar"]
}
fn calculate(&self, stats: &TextStatistics, language: &Language) -> FormulaResult {
let w = stats.word_count.max(1) as f64;
let s = stats.sentence_count.max(1) as f64;
let asl = w / s;
let avg_letters = stats.letter_count as f64 / w;
let hard_words_pct = (stats.polysyllable_count as f64 / w) * 100.0;
let score = 200.0 - 2.0 * asl - 1.5 * avg_letters - 0.4 * hard_words_pct;
FormulaResult {
formula_name: self.name().to_string(),
language_code: language.code.clone(),
score: round_to(score, 1),
grade_level: None,
interpretation: interpret_osman(score).to_string(),
inputs: inputs(&[
("asl", round_to(asl, 2)),
("avgLetters", round_to(avg_letters, 2)),
("hardWordsPct", round_to(hard_words_pct, 2)),
]),
}
}
}
fn interpret_osman(score: f64) -> &'static str {
if score >= 90.0 {
"Very Easy"
} else if score >= 70.0 {
"Easy"
} else if score >= 50.0 {
"Standard"
} else if score >= 30.0 {
"Difficult"
} else {
"Very Difficult"
}
}