Files
Jyotisha/tests/test_varga_bphs.py
2026-07-20 00:43:25 +08:00

192 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""Regression tests for BPHS divisional chart mappings."""
from __future__ import annotations
from hypothesis import given
from hypothesis import strategies as st
from varga import (
SIGNS,
calc_22nd_drekkana,
calc_64th_navamsa,
calc_all_vargas,
calc_bhrigu_bindu,
calc_sarpa_drekkana,
calc_varga,
varga_map,
)
def navamsa_ref(lon: float) -> int:
sign_index = int((lon % 360) / 30) % 12
degree_in_sign = (lon % 360) - sign_index * 30
part_index = int(degree_in_sign / (30 / 9))
if sign_index % 3 == 0:
start = sign_index
elif sign_index % 3 == 1:
start = (sign_index + 8) % 12
else:
start = (sign_index + 4) % 12
return (start + part_index) % 12
def dasamsa_ref(lon: float) -> int:
sign_index = int((lon % 360) / 30) % 12
degree_in_sign = (lon % 360) - sign_index * 30
part_index = int(degree_in_sign / 3)
start = sign_index if sign_index % 2 == 0 else (sign_index + 8) % 12
return (start + part_index) % 12
def drekkana_ref(lon: float) -> int:
sign_index = int((lon % 360) / 30) % 12
degree_in_sign = (lon % 360) - sign_index * 30
part_index = int(degree_in_sign / 10)
return (sign_index + part_index * 4) % 12
def chaturthamsa_ref(lon: float) -> int:
sign_index = int((lon % 360) / 30) % 12
degree_in_sign = (lon % 360) - sign_index * 30
part_index = int(degree_in_sign / (30 / 4))
return (sign_index + part_index * 3) % 12
@given(st.floats(min_value=0, max_value=359.999999, allow_nan=False, allow_infinity=False))
def test_navamsa_matches_bphs_reference(lon: float) -> None:
result = calc_varga(lon, 9)
assert result["sign_idx"] == navamsa_ref(lon)
assert result["sign"] == SIGNS[navamsa_ref(lon)]
assert 0 <= result["degree_in_sign"] < 30
@given(st.floats(min_value=0, max_value=359.999999, allow_nan=False, allow_infinity=False))
def test_dasamsa_matches_bphs_reference(lon: float) -> None:
result = calc_varga(lon, 10)
assert result["sign_idx"] == dasamsa_ref(lon)
assert result["sign"] == SIGNS[dasamsa_ref(lon)]
assert 0 <= result["degree_in_sign"] < 30
@given(st.floats(min_value=0, max_value=359.999999, allow_nan=False, allow_infinity=False))
def test_drekkana_uses_same_plus_four_plus_eight(lon: float) -> None:
result = calc_varga(lon, 3)
assert result["sign_idx"] == drekkana_ref(lon)
@given(st.floats(min_value=0, max_value=359.999999, allow_nan=False, allow_infinity=False))
def test_chaturthamsa_matches_pyjhora_parashara_reference(lon: float) -> None:
result = calc_varga(lon, 4)
assert result["sign_idx"] == chaturthamsa_ref(lon)
assert result["sign"] == SIGNS[chaturthamsa_ref(lon)]
assert 0 <= result["degree_in_sign"] < 30
def test_chaturthamsa_reference_boundary_examples() -> None:
assert [varga_map(0, part, 4) for part in range(4)] == [0, 3, 6, 9]
assert [varga_map(8, part, 4) for part in range(4)] == [8, 11, 2, 5]
assert [varga_map(11, part, 4) for part in range(4)] == [11, 2, 5, 8]
def test_varga_map_boundary_examples() -> None:
assert varga_map(0, 0, 9) == 0 # Aries Navamsa starts Aries
assert varga_map(1, 0, 9) == 9 # Taurus Navamsa starts Capricorn (9th from sign)
assert varga_map(2, 0, 9) == 6 # Gemini Navamsa starts Libra (5th from sign)
assert varga_map(1, 0, 10) == 9 # Taurus Dasamsa starts Capricorn
assert varga_map(0, 2, 3) == 8 # Aries third Drekkana = Sagittarius
def test_rudramsa_d11_is_supported_by_native_varga_map() -> None:
assert varga_map(0, 0, 11) == 0
assert varga_map(0, 10, 11) == 10
assert varga_map(1, 0, 11) == 9
assert varga_map(1, 10, 11) == 7
result = calc_varga(35.0, 11)
assert result["sign"] == "Aquarius"
assert 0 <= result["degree_in_sign"] < 30
def test_native_varga_map_supports_declared_high_value_vargas() -> None:
for div in (2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24, 60, 81, 108, 144):
result = calc_varga(35.0, div)
assert result["sign"] in SIGNS
assert 0 <= result["degree_in_sign"] < 30
def test_default_varga_batch_includes_declared_vargas() -> None:
charts = calc_all_vargas({"Moon": 35.0}, 10.0)
for key in ("D5_Panchamsa", "D6_Shashthamsa", "D8_Ashtamsa", "D11_Rudramsa", "D81_Navamsa-Navamsa", "D108_Dwadasamsa-Navamsa", "D144_Dwadasamsa-Dwadasamsa"):
assert key in charts
assert charts["D11_Rudramsa"]["Moon"]["sign"] == "Aquarius"
def test_navamsa_matches_user_jhora_pdf_reference_chart() -> None:
"""Regression from private_chart_reference.pdf: JHora-style D9 table for public sample birth datetime San Francisco."""
expected = {
132.355025: "Cancer", # Ascendant 12 Leo 21'18.09"
3.5226611111111112: "Taurus",
311.78995555555554: "Capricorn",
91.33091388888889: "Cancer",
338.5488: "Virgo",
163.83150833333335: "Taurus",
340.5554638888889: "Libra",
304.3033805555556: "Scorpio",
231.04509444444443: "Capricorn",
51.045094444444445: "Cancer",
}
for longitude, expected_sign in expected.items():
assert calc_varga(longitude, 9)["sign"] == expected_sign
def test_64th_navamsa_counts_forward_from_moon_navamsa() -> None:
moon_lon = 42.3
result = calc_64th_navamsa(moon_lon)
expected_sign_idx = (navamsa_ref(moon_lon) + 63) % 12
assert result["sign_idx"] == expected_sign_idx
assert result["sign"] == SIGNS[expected_sign_idx]
assert result["offset_from_moon_navamsa"] == 64
def test_22nd_drekkana_counts_forward_from_lagna_drekkana() -> None:
asc_lon = 25.5
result = calc_22nd_drekkana(asc_lon)
expected_sign_idx = (drekkana_ref(asc_lon) + 21) % 12
assert result["sign_idx"] == expected_sign_idx
assert result["sign"] == SIGNS[expected_sign_idx]
assert result["offset_from_lagna_drekkana"] == 22
def test_bhrigu_bindu_uses_rahu_to_moon_arc_midpoint() -> None:
result = calc_bhrigu_bindu(270.5, 160.2333333333)
assert result["longitude"] == 215.3667
assert result["sign"] == "Scorpio"
assert result["degree_in_sign"] == 5.3667
def test_bhrigu_bindu_handles_wraparound_across_zero_aries() -> None:
result = calc_bhrigu_bindu(10.0, 350.0)
assert result["longitude"] == 0.0
assert result["sign"] == "Aries"
assert result["degree_in_sign"] == 0.0
def test_sarpa_drekkana_detects_classical_sensitive_ranges() -> None:
cancer_second = calc_sarpa_drekkana(105.0)
scorpio_first = calc_sarpa_drekkana(215.0)
pisces_third = calc_sarpa_drekkana(355.0)
safe_aries = calc_sarpa_drekkana(15.0)
assert cancer_second["is_sarpa_drekkana"] is True
assert cancer_second["sign"] == "Cancer"
assert cancer_second["drekkana_number"] == 2
assert scorpio_first["is_sarpa_drekkana"] is True
assert scorpio_first["sign"] == "Scorpio"
assert scorpio_first["drekkana_number"] == 1
assert pisces_third["is_sarpa_drekkana"] is True
assert pisces_third["sign"] == "Pisces"
assert pisces_third["drekkana_number"] == 3
assert safe_aries["is_sarpa_drekkana"] is False