use crate::enums::PersonalityProfile;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Hexaco {
openness: f32,
conscientiousness: f32,
extraversion: f32,
agreeableness: f32,
neuroticism: f32,
honesty_humility: f32,
}
impl Hexaco {
#[must_use]
pub fn new() -> Self {
Hexaco {
openness: 0.0,
conscientiousness: 0.0,
extraversion: 0.0,
agreeableness: 0.0,
neuroticism: 0.0,
honesty_humility: 0.0,
}
}
#[must_use]
pub fn from_profile(profile: PersonalityProfile) -> Self {
Hexaco {
openness: profile.openness() * 2.0 - 1.0,
conscientiousness: profile.conscientiousness() * 2.0 - 1.0,
extraversion: profile.extraversion() * 2.0 - 1.0,
agreeableness: profile.agreeableness() * 2.0 - 1.0,
neuroticism: profile.neuroticism() * 2.0 - 1.0,
honesty_humility: profile.honesty_humility() * 2.0 - 1.0,
}
}
#[must_use]
pub fn uniform(value: f32) -> Self {
let clamped = value.clamp(-1.0, 1.0);
Hexaco {
openness: clamped,
conscientiousness: clamped,
extraversion: clamped,
agreeableness: clamped,
neuroticism: clamped,
honesty_humility: clamped,
}
}
#[must_use]
pub fn with_openness(mut self, value: f32) -> Self {
self.openness = value.clamp(-1.0, 1.0);
self
}
#[must_use]
pub fn with_conscientiousness(mut self, value: f32) -> Self {
self.conscientiousness = value.clamp(-1.0, 1.0);
self
}
#[must_use]
pub fn with_extraversion(mut self, value: f32) -> Self {
self.extraversion = value.clamp(-1.0, 1.0);
self
}
#[must_use]
pub fn with_agreeableness(mut self, value: f32) -> Self {
self.agreeableness = value.clamp(-1.0, 1.0);
self
}
#[must_use]
pub fn with_neuroticism(mut self, value: f32) -> Self {
self.neuroticism = value.clamp(-1.0, 1.0);
self
}
#[must_use]
pub fn with_honesty_humility(mut self, value: f32) -> Self {
self.honesty_humility = value.clamp(-1.0, 1.0);
self
}
#[must_use]
pub fn openness(&self) -> f32 {
self.openness
}
#[must_use]
pub fn conscientiousness(&self) -> f32 {
self.conscientiousness
}
#[must_use]
pub fn extraversion(&self) -> f32 {
self.extraversion
}
#[must_use]
pub fn agreeableness(&self) -> f32 {
self.agreeableness
}
#[must_use]
pub fn neuroticism(&self) -> f32 {
self.neuroticism
}
#[must_use]
pub fn emotionality(&self) -> f32 {
self.neuroticism
}
#[must_use]
pub fn honesty_humility(&self) -> f32 {
self.honesty_humility
}
pub fn set_openness(&mut self, value: f32) {
self.openness = value.clamp(-1.0, 1.0);
}
pub fn set_conscientiousness(&mut self, value: f32) {
self.conscientiousness = value.clamp(-1.0, 1.0);
}
pub fn set_extraversion(&mut self, value: f32) {
self.extraversion = value.clamp(-1.0, 1.0);
}
pub fn set_agreeableness(&mut self, value: f32) {
self.agreeableness = value.clamp(-1.0, 1.0);
}
pub fn set_neuroticism(&mut self, value: f32) {
self.neuroticism = value.clamp(-1.0, 1.0);
}
pub fn set_honesty_humility(&mut self, value: f32) {
self.honesty_humility = value.clamp(-1.0, 1.0);
}
}
impl Default for Hexaco {
fn default() -> Self {
Hexaco::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_creates_neutral_factors() {
let hexaco = Hexaco::new();
assert!((hexaco.openness() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.conscientiousness() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.extraversion() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.agreeableness() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.neuroticism() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.honesty_humility() - 0.0).abs() < f32::EPSILON);
}
#[test]
fn from_profile_balanced_is_neutral() {
let hexaco = Hexaco::from_profile(PersonalityProfile::Balanced);
assert!((hexaco.openness() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.conscientiousness() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.extraversion() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.agreeableness() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.neuroticism() - 0.0).abs() < f32::EPSILON);
assert!((hexaco.honesty_humility() - 0.0).abs() < f32::EPSILON);
}
#[test]
fn from_profile_anxious_has_high_neuroticism() {
let hexaco = Hexaco::from_profile(PersonalityProfile::Anxious);
assert!(hexaco.neuroticism() > 0.5);
}
#[test]
fn from_profile_leader_characteristics() {
let hexaco = Hexaco::from_profile(PersonalityProfile::Leader);
assert!(hexaco.extraversion() > 0.5);
assert!(hexaco.conscientiousness() > 0.5);
assert!(hexaco.neuroticism() < -0.3);
}
#[test]
fn uniform_sets_all_factors() {
let hexaco = Hexaco::uniform(0.5);
assert!((hexaco.openness() - 0.5).abs() < f32::EPSILON);
assert!((hexaco.conscientiousness() - 0.5).abs() < f32::EPSILON);
assert!((hexaco.extraversion() - 0.5).abs() < f32::EPSILON);
assert!((hexaco.agreeableness() - 0.5).abs() < f32::EPSILON);
assert!((hexaco.neuroticism() - 0.5).abs() < f32::EPSILON);
assert!((hexaco.honesty_humility() - 0.5).abs() < f32::EPSILON);
}
#[test]
fn uniform_clamps_values() {
let high = Hexaco::uniform(2.0);
assert!((high.openness() - 1.0).abs() < f32::EPSILON);
let low = Hexaco::uniform(-2.0);
assert!((low.openness() - (-1.0)).abs() < f32::EPSILON);
}
#[test]
fn builder_methods_work() {
let hexaco = Hexaco::new()
.with_openness(0.7)
.with_conscientiousness(0.5)
.with_extraversion(-0.3)
.with_agreeableness(0.2)
.with_neuroticism(-0.5)
.with_honesty_humility(0.8);
assert!((hexaco.openness() - 0.7).abs() < f32::EPSILON);
assert!((hexaco.conscientiousness() - 0.5).abs() < f32::EPSILON);
assert!((hexaco.extraversion() - (-0.3)).abs() < f32::EPSILON);
assert!((hexaco.agreeableness() - 0.2).abs() < f32::EPSILON);
assert!((hexaco.neuroticism() - (-0.5)).abs() < f32::EPSILON);
assert!((hexaco.honesty_humility() - 0.8).abs() < f32::EPSILON);
}
#[test]
fn builder_methods_clamp_values() {
let hexaco = Hexaco::new().with_openness(2.0).with_neuroticism(-2.0);
assert!((hexaco.openness() - 1.0).abs() < f32::EPSILON);
assert!((hexaco.neuroticism() - (-1.0)).abs() < f32::EPSILON);
}
#[test]
fn setters_work() {
let mut hexaco = Hexaco::new();
hexaco.set_openness(0.7);
assert!((hexaco.openness() - 0.7).abs() < f32::EPSILON);
hexaco.set_conscientiousness(0.5);
assert!((hexaco.conscientiousness() - 0.5).abs() < f32::EPSILON);
hexaco.set_extraversion(-0.3);
assert!((hexaco.extraversion() - (-0.3)).abs() < f32::EPSILON);
hexaco.set_agreeableness(0.2);
assert!((hexaco.agreeableness() - 0.2).abs() < f32::EPSILON);
hexaco.set_neuroticism(-0.5);
assert!((hexaco.neuroticism() - (-0.5)).abs() < f32::EPSILON);
hexaco.set_honesty_humility(0.8);
assert!((hexaco.honesty_humility() - 0.8).abs() < f32::EPSILON);
}
#[test]
fn setters_clamp_values() {
let mut hexaco = Hexaco::new();
hexaco.set_openness(5.0);
assert!((hexaco.openness() - 1.0).abs() < f32::EPSILON);
hexaco.set_neuroticism(-5.0);
assert!((hexaco.neuroticism() - (-1.0)).abs() < f32::EPSILON);
}
#[test]
fn default_is_neutral() {
let hexaco = Hexaco::default();
assert!((hexaco.openness() - 0.0).abs() < f32::EPSILON);
}
#[test]
fn clone_and_equality() {
let hexaco1 = Hexaco::new().with_openness(0.5);
let hexaco2 = hexaco1.clone();
assert_eq!(hexaco1, hexaco2);
}
#[test]
fn debug_format() {
let hexaco = Hexaco::new();
let debug = format!("{:?}", hexaco);
assert!(debug.contains("Hexaco"));
}
#[test]
fn from_profile_rebel_characteristics() {
let hexaco = Hexaco::from_profile(PersonalityProfile::Rebel);
assert!(hexaco.agreeableness() < -0.5);
assert!(hexaco.honesty_humility() < -0.3);
}
#[test]
fn from_profile_introverted_characteristics() {
let hexaco = Hexaco::from_profile(PersonalityProfile::Introverted);
assert!(hexaco.extraversion() < -0.5);
}
#[test]
fn emotionality_is_alias_for_neuroticism() {
let hexaco = Hexaco::new().with_neuroticism(0.7);
assert!((hexaco.emotionality() - 0.7).abs() < f32::EPSILON);
assert!((hexaco.emotionality() - hexaco.neuroticism()).abs() < f32::EPSILON);
}
}