1use serde::Serialize;
4
5use crate::aspects::Point;
6use crate::pyfloat;
7use crate::symbolic::symbolic_degree_number;
8use crate::zodiac::{determine_house, longitude_to_zodiac};
9
10struct LotDefinition {
11 name: &'static str,
12 symbol: &'static str,
13 day: (&'static str, &'static str, &'static str),
15}
16
17const LOTS: [LotDefinition; 11] = [
18 LotDefinition {
19 name: "Fortune",
20 symbol: "⊗",
21 day: ("Ascendant", "Moon", "Sun"),
22 },
23 LotDefinition {
24 name: "Spirit",
25 symbol: "🜕",
26 day: ("Ascendant", "Sun", "Moon"),
27 },
28 LotDefinition {
29 name: "Eros",
30 symbol: "🏹",
31 day: ("Ascendant", "Venus", "Spirit"),
32 },
33 LotDefinition {
34 name: "Necessity",
35 symbol: "⛓",
36 day: ("Ascendant", "Fortune", "Mercury"),
37 },
38 LotDefinition {
39 name: "Courage",
40 symbol: "🛡",
41 day: ("Ascendant", "Fortune", "Mars"),
42 },
43 LotDefinition {
44 name: "Victory",
45 symbol: "🏆",
46 day: ("Ascendant", "Jupiter", "Fortune"),
47 },
48 LotDefinition {
49 name: "Nemesis",
50 symbol: "⚖",
51 day: ("Ascendant", "Fortune", "Saturn"),
52 },
53 LotDefinition {
54 name: "Marriage",
55 symbol: "💍",
56 day: ("Ascendant", "Venus", "Saturn"),
57 },
58 LotDefinition {
59 name: "Commerce",
60 symbol: "☤",
61 day: ("Ascendant", "Mercury", "Sun"),
62 },
63 LotDefinition {
64 name: "Sickness",
65 symbol: "⚕",
66 day: ("Ascendant", "Mars", "Saturn"),
67 },
68 LotDefinition {
69 name: "Death",
70 symbol: "☠",
71 day: ("Ascendant", "House 8", "Moon"),
72 },
73];
74
75#[derive(Debug, Clone, PartialEq, Serialize)]
77pub struct Lot {
78 pub name: &'static str,
80 pub symbol: &'static str,
82 pub sign: &'static str,
84 pub sign_symbol: &'static str,
86 pub degree: i64,
88 pub minute: i64,
90 pub ecliptic_longitude: f64,
92 pub house: u8,
94 pub formula_used: String,
96 pub is_diurnal: bool,
98 pub symbolic_degree: i64,
100}
101
102pub fn arabic_parts(points: &[Point], cusps: &[f64]) -> Vec<Lot> {
105 let find = |name: &str| points.iter().find(|p| p.name == name).map(|p| p.longitude);
106 let (Some(sun), Some(_)) = (find("Sun"), find("Ascendant")) else {
107 return Vec::new();
108 };
109 let sun_house = if cusps.is_empty() {
110 1
111 } else {
112 determine_house(sun, cusps)
113 };
114 let is_day = sun_house >= 7;
115 let period = if is_day { "Day" } else { "Night" };
116
117 let mut computed: Vec<(&str, f64)> = Vec::new();
118 let mut out = Vec::new();
119 for lot in &LOTS {
120 let (base, a, b) = if is_day {
121 lot.day
122 } else {
123 (lot.day.0, lot.day.2, lot.day.1)
124 };
125 let lon_of = |name: &str| {
126 if let Some((_, lon)) = computed.iter().find(|(n, _)| *n == name) {
127 return *lon;
128 }
129 if let Some(lon) = find(name) {
130 return lon;
131 }
132 if name == "House 8" && cusps.len() >= 8 {
133 return cusps[7];
134 }
135 0.0
136 };
137 let lon = pyfloat::rem(lon_of(base) + lon_of(a) - lon_of(b), 360.0);
138 computed.push((lot.name, lon));
139 let z = longitude_to_zodiac(lon);
140 let formula_used = if base == "Ascendant" {
141 format!("ASC + {a} - {b} ({period})")
142 } else {
143 format!("{base} + {a} - {b} ({period})")
144 };
145 out.push(Lot {
146 name: lot.name,
147 symbol: lot.symbol,
148 sign: z.sign.name,
149 sign_symbol: z.sign.symbol,
150 degree: z.degree,
151 minute: z.minute,
152 ecliptic_longitude: lon,
153 house: determine_house(lon, cusps),
154 formula_used,
155 is_diurnal: is_day,
156 symbolic_degree: symbolic_degree_number(z.degree, z.minute),
157 });
158 }
159 out
160}