use bitflags::bitflags;
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(transparent)]
pub struct LineageId(pub u32);
impl LineageId {
pub const NULL: Self = Self(u32::MAX);
#[inline]
pub fn is_valid(self) -> bool {
self != Self::NULL
}
#[inline]
pub fn index(self) -> usize {
self.0 as usize
}
}
impl From<u32> for LineageId {
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<usize> for LineageId {
fn from(v: usize) -> Self {
Self(v as u32)
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct LineageFlags: u32 {
const ACTIVE = 1 << 0;
const CONSCIOUS = 1 << 1;
const PROTECTED = 1 << 2;
const DIRTY = 1 << 3;
const PINNED = 1 << 4;
}
}
impl Default for LineageFlags {
fn default() -> Self {
Self::ACTIVE
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[repr(C, align(32))]
pub struct Lineage {
pub energy: f32,
pub threshold: f32,
pub decay_rate: f32,
pub rigidity: f32,
pub last_access: u64,
pub flags: LineageFlags,
pub head_index: u32,
}
impl Default for Lineage {
fn default() -> Self {
Self {
energy: 1.0,
threshold: 0.5,
decay_rate: 0.001,
rigidity: 0.5,
last_access: now_nanos(),
flags: LineageFlags::ACTIVE,
head_index: u32::MAX,
}
}
}
impl Lineage {
pub fn new(energy: f32) -> Self {
Self {
energy,
last_access: now_nanos(),
..Default::default()
}
}
pub fn with_config(energy: f32, threshold: f32, decay_rate: f32) -> Self {
Self {
energy,
threshold,
decay_rate,
last_access: now_nanos(),
..Default::default()
}
}
#[inline]
pub fn is_conscious(&self) -> bool {
self.energy >= self.threshold
}
#[inline]
pub fn is_active(&self) -> bool {
self.flags.contains(LineageFlags::ACTIVE)
}
#[inline]
pub fn is_protected(&self) -> bool {
self.flags.contains(LineageFlags::PROTECTED)
}
pub fn current_energy(&self) -> f32 {
if self.is_protected() {
return self.energy;
}
let elapsed_secs = elapsed_seconds(self.last_access);
self.energy * (-self.decay_rate * elapsed_secs).exp()
}
pub fn stimulate(&mut self, delta: f32) {
self.energy = (self.current_energy() + delta).clamp(0.0, 1.0);
self.last_access = now_nanos();
self.flags.insert(LineageFlags::DIRTY);
if self.is_conscious() {
self.flags.insert(LineageFlags::CONSCIOUS);
} else {
self.flags.remove(LineageFlags::CONSCIOUS);
}
}
pub fn touch(&mut self) {
self.energy = self.current_energy();
self.last_access = now_nanos();
}
}
pub struct PsycheArena {
data: Vec<Lineage>,
count: usize,
free_list: Vec<LineageId>,
id_map: rustc_hash::FxHashMap<u64, LineageId>,
}
impl PsycheArena {
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
count: 0,
free_list: Vec::new(),
id_map: rustc_hash::FxHashMap::default(),
}
}
#[inline]
pub fn len(&self) -> usize {
self.count
}
#[inline]
pub fn is_empty(&self) -> bool {
self.count == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn alloc(&mut self, lineage: Lineage) -> LineageId {
let id = if let Some(recycled) = self.free_list.pop() {
self.data[recycled.index()] = lineage;
recycled
} else {
let id = LineageId(self.data.len() as u32);
self.data.push(lineage);
id
};
self.count += 1;
id
}
pub fn alloc_with_key(&mut self, key: u64, lineage: Lineage) -> LineageId {
let id = self.alloc(lineage);
self.id_map.insert(key, id);
id
}
#[inline]
pub fn get(&self, id: LineageId) -> Option<&Lineage> {
self.data.get(id.index()).filter(|l| l.is_active())
}
#[inline]
pub fn get_mut(&mut self, id: LineageId) -> Option<&mut Lineage> {
self.data.get_mut(id.index()).filter(|l| l.is_active())
}
pub fn lookup(&self, key: u64) -> Option<LineageId> {
self.id_map.get(&key).copied()
}
pub fn free(&mut self, id: LineageId) -> bool {
if let Some(lineage) = self.data.get_mut(id.index()) {
if lineage.is_active() {
lineage.flags.remove(LineageFlags::ACTIVE);
self.free_list.push(id);
self.count -= 1;
return true;
}
}
false
}
pub fn iter(&self) -> impl Iterator<Item = (LineageId, &Lineage)> {
self.data
.iter()
.enumerate()
.filter(|(_, l)| l.is_active())
.map(|(i, l)| (LineageId(i as u32), l))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (LineageId, &mut Lineage)> {
self.data
.iter_mut()
.enumerate()
.filter(|(_, l)| l.is_active())
.map(|(i, l)| (LineageId(i as u32), l))
}
pub fn as_slice(&self) -> &[Lineage] {
&self.data
}
}
#[inline]
fn now_nanos() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0)
}
#[inline]
fn elapsed_seconds(timestamp: u64) -> f32 {
let now = now_nanos();
if now > timestamp {
((now - timestamp) as f64 / 1_000_000_000.0) as f32
} else {
0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lineage_default() {
let l = Lineage::default();
assert_eq!(l.energy, 1.0);
assert_eq!(l.threshold, 0.5);
assert!(l.is_conscious());
assert!(l.is_active());
}
#[test]
fn test_lineage_stimulate() {
let mut l = Lineage::new(0.3);
l.threshold = 0.5;
assert!(!l.is_conscious());
l.stimulate(0.3);
assert!(l.is_conscious());
}
#[test]
fn test_psyche_arena_alloc() {
let mut arena = PsycheArena::with_capacity(100);
let id = arena.alloc(Lineage::new(0.8));
assert_eq!(arena.len(), 1);
assert!(arena.get(id).is_some());
assert_eq!(arena.get(id).unwrap().energy, 0.8);
}
#[test]
fn test_psyche_arena_free_and_recycle() {
let mut arena = PsycheArena::with_capacity(100);
let id1 = arena.alloc(Lineage::new(0.5));
let _id2 = arena.alloc(Lineage::new(0.7));
assert_eq!(arena.len(), 2);
arena.free(id1);
assert_eq!(arena.len(), 1);
assert!(arena.get(id1).is_none());
let id3 = arena.alloc(Lineage::new(0.9));
assert_eq!(id3, id1); assert_eq!(arena.len(), 2);
}
#[test]
fn test_lineage_id_null() {
assert!(!LineageId::NULL.is_valid());
assert!(LineageId(0).is_valid());
}
}