libaditya 0.3.4__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. libaditya/__init__.py +46 -0
  2. libaditya/_version.py +29 -0
  3. libaditya/calc/__init__.py +9 -0
  4. libaditya/calc/api.py +30 -0
  5. libaditya/calc/avasthas.py +346 -0
  6. libaditya/calc/docs/jaimini-get-arch.md +95 -0
  7. libaditya/calc/hellenistic.py +37 -0
  8. libaditya/calc/jaimini.py +335 -0
  9. libaditya/calc/jaimini_get.py +240 -0
  10. libaditya/calc/kala.py +73 -0
  11. libaditya/calc/panchanga.py +422 -0
  12. libaditya/calc/rashi.py +612 -0
  13. libaditya/calc/returns.py +50 -0
  14. libaditya/calc/swe_functions.py +118 -0
  15. libaditya/calc/varga.py +287 -0
  16. libaditya/calc/vimshottari.py +431 -0
  17. libaditya/cards/__init__.py +5 -0
  18. libaditya/cards/card.py +90 -0
  19. libaditya/cards/cards_constants.py +115 -0
  20. libaditya/cards/cards_of_truth.py +323 -0
  21. libaditya/cards/cot.py +142 -0
  22. libaditya/cards/deck.py +52 -0
  23. libaditya/charts/__init__.py +6 -0
  24. libaditya/charts/api.py +36 -0
  25. libaditya/charts/bodygraph.py +80 -0
  26. libaditya/charts/chart.py +286 -0
  27. libaditya/charts/jaimini.py +83 -0
  28. libaditya/charts/tajika.py +36 -0
  29. libaditya/constants.py +1776 -0
  30. libaditya/draw/__init__.py +20 -0
  31. libaditya/draw/draw_bodygraph.py +489 -0
  32. libaditya/draw/draw_sbc.py +712 -0
  33. libaditya/draw/themes/default-theme.hd +53 -0
  34. libaditya/ephe/ast0/se00010s.se1 +0 -0
  35. libaditya/ephe/seas_00.se1 +0 -0
  36. libaditya/ephe/seas_06.se1 +0 -0
  37. libaditya/ephe/seas_12.se1 +0 -0
  38. libaditya/ephe/seas_18.se1 +0 -0
  39. libaditya/ephe/seas_24.se1 +0 -0
  40. libaditya/ephe/seas_30.se1 +0 -0
  41. libaditya/ephe/seas_36.se1 +0 -0
  42. libaditya/ephe/seas_42.se1 +0 -0
  43. libaditya/ephe/seas_48.se1 +0 -0
  44. libaditya/ephe/seasm06.se1 +0 -0
  45. libaditya/ephe/seasm12.se1 +0 -0
  46. libaditya/ephe/seasm18.se1 +0 -0
  47. libaditya/ephe/seasm24.se1 +0 -0
  48. libaditya/ephe/seasm30.se1 +0 -0
  49. libaditya/ephe/seasm36.se1 +0 -0
  50. libaditya/ephe/seasm42.se1 +0 -0
  51. libaditya/ephe/seasm48.se1 +0 -0
  52. libaditya/ephe/seasm54.se1 +0 -0
  53. libaditya/ephe/sefstars.txt +4245 -0
  54. libaditya/ephe/semo_00.se1 +0 -0
  55. libaditya/ephe/semo_06.se1 +0 -0
  56. libaditya/ephe/semo_12.se1 +0 -0
  57. libaditya/ephe/semo_18.se1 +0 -0
  58. libaditya/ephe/semo_24.se1 +0 -0
  59. libaditya/ephe/semo_30.se1 +0 -0
  60. libaditya/ephe/semo_36.se1 +0 -0
  61. libaditya/ephe/semo_42.se1 +0 -0
  62. libaditya/ephe/semo_48.se1 +0 -0
  63. libaditya/ephe/semom06.se1 +0 -0
  64. libaditya/ephe/semom12.se1 +0 -0
  65. libaditya/ephe/semom18.se1 +0 -0
  66. libaditya/ephe/semom24.se1 +0 -0
  67. libaditya/ephe/semom30.se1 +0 -0
  68. libaditya/ephe/semom36.se1 +0 -0
  69. libaditya/ephe/semom42.se1 +0 -0
  70. libaditya/ephe/semom48.se1 +0 -0
  71. libaditya/ephe/semom54.se1 +0 -0
  72. libaditya/ephe/sepl_00.se1 +0 -0
  73. libaditya/ephe/sepl_06.se1 +0 -0
  74. libaditya/ephe/sepl_12.se1 +0 -0
  75. libaditya/ephe/sepl_18.se1 +0 -0
  76. libaditya/ephe/sepl_24.se1 +0 -0
  77. libaditya/ephe/sepl_30.se1 +0 -0
  78. libaditya/ephe/sepl_36.se1 +0 -0
  79. libaditya/ephe/sepl_42.se1 +0 -0
  80. libaditya/ephe/sepl_48.se1 +0 -0
  81. libaditya/ephe/seplm06.se1 +0 -0
  82. libaditya/ephe/seplm12.se1 +0 -0
  83. libaditya/ephe/seplm18.se1 +0 -0
  84. libaditya/ephe/seplm24.se1 +0 -0
  85. libaditya/ephe/seplm30.se1 +0 -0
  86. libaditya/ephe/seplm36.se1 +0 -0
  87. libaditya/ephe/seplm42.se1 +0 -0
  88. libaditya/ephe/seplm48.se1 +0 -0
  89. libaditya/ephe/seplm54.se1 +0 -0
  90. libaditya/hd/__init__.py +8 -0
  91. libaditya/hd/calc.py +55 -0
  92. libaditya/hd/constants.py +127 -0
  93. libaditya/hd/longitude.py +271 -0
  94. libaditya/objects/__init__.py +19 -0
  95. libaditya/objects/celestial_object.py +206 -0
  96. libaditya/objects/context.py +133 -0
  97. libaditya/objects/cusps.py +259 -0
  98. libaditya/objects/julian_day.py +265 -0
  99. libaditya/objects/location.py +167 -0
  100. libaditya/objects/longitude.py +987 -0
  101. libaditya/objects/nakshatras.py +285 -0
  102. libaditya/objects/planets.py +2059 -0
  103. libaditya/objects/shadbala.py +398 -0
  104. libaditya/objects/signs.py +690 -0
  105. libaditya/objects/swe_functions.py +75 -0
  106. libaditya/print_functions.py +548 -0
  107. libaditya/read.py +665 -0
  108. libaditya/stars/__init__.py +4 -0
  109. libaditya/stars/fixed_star.py +216 -0
  110. libaditya/stars/make_swe_stars.py +407 -0
  111. libaditya/stars/star-sign-boundaries +44 -0
  112. libaditya/stars/stellarium/__init__.py +6 -0
  113. libaditya/stars/stellarium/remote_control/__init__.py +22 -0
  114. libaditya/stars/stellarium/remote_control/location.py +82 -0
  115. libaditya/stars/stellarium/remote_control/locationsearch.py +35 -0
  116. libaditya/stars/stellarium/remote_control/main.py +165 -0
  117. libaditya/stars/stellarium/remote_control/objects.py +63 -0
  118. libaditya/stars/stellarium/remote_control/scripts.py +81 -0
  119. libaditya/stars/stellarium/remote_control/simbad.py +20 -0
  120. libaditya/stars/stellarium/remote_control/stelaction.py +31 -0
  121. libaditya/stars/stellarium/remote_control/stelproperty.py +30 -0
  122. libaditya/stars/stellarium/remote_control/view.py +85 -0
  123. libaditya/stars/stellarium/stellarium.py +92 -0
  124. libaditya/stars/the_stars.py +522 -0
  125. libaditya/stars/utilities.py +539 -0
  126. libaditya/utils.py +431 -0
  127. libaditya/write.py +67 -0
  128. libaditya-0.3.4.dist-info/METADATA +588 -0
  129. libaditya-0.3.4.dist-info/RECORD +131 -0
  130. libaditya-0.3.4.dist-info/WHEEL +5 -0
  131. libaditya-0.3.4.dist-info/licenses/LICENSE +661 -0
@@ -0,0 +1,690 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import swisseph as swe
19
+ from prettytable import PrettyTable
20
+ from typing import Self
21
+
22
+ from rich import box
23
+ from rich.table import Table
24
+ from rich.console import Console
25
+
26
+ from libaditya import constants as const
27
+ from libaditya import utils
28
+
29
+ from .planets import *
30
+ from .cusps import Cusp, Cusps
31
+ from .context import EphContext
32
+
33
+ class Sign:
34
+
35
+ def __init__(self, number, planets, cusps, context, master):
36
+ self.context = context
37
+ self._planets = planets
38
+ self._cusps = cusps
39
+ self._objects = self.init_objects()
40
+ self._sign_index = (number-1)%12
41
+ self._sign_name = const.names[self.context.names_type][self.context.sign_names][self.sign_index()]
42
+ self._id = number
43
+ self.master = master
44
+ self._lajjitaadi_avasthas = {}
45
+ # a dictionary that defines our rashi aspects, {int: (int,int,int)}
46
+
47
+ def sign_index(self):
48
+ return self._sign_index
49
+
50
+ def sign_name(self):
51
+ return self._sign_name
52
+
53
+ def name(self):
54
+ return self.sign_name()
55
+
56
+ def sign(self):
57
+ return self._id
58
+
59
+ def Self(self):
60
+ return self
61
+
62
+ def planets(self) -> [Planet]:
63
+ """
64
+ Planet is anything that can possibly be a Planet
65
+ return a list of Planets that are in this sign
66
+ """
67
+ return self._planets
68
+
69
+ def cusps(self):
70
+ return self._cusps
71
+
72
+ def lajjitaadi_avasthas(self):
73
+ return self._lajjitaadi_avasthas
74
+
75
+ def lord(self) -> str:
76
+ return const.lords[self.sign()]
77
+
78
+ def gender(self):
79
+ if utils.odd(self.sign()):
80
+ return "M"
81
+ if utils.even(self.sign()):
82
+ return "F"
83
+
84
+ def rashis_from_lagna(self):
85
+ """
86
+ lagna can be a Sign class or a sign number
87
+ how many signs apart are this one sign and other_sign
88
+ if self=12 and other=1 -> (1-12)%12 = 1
89
+ used for temporary relationships
90
+ """
91
+ lagna = self.master.lagna().sign()
92
+ return ((self.sign() - lagna)%12)+1
93
+
94
+ def house_type(self):
95
+ """
96
+ is this an Angle, Panaphara, or Apoklima
97
+ this uses signs from lagna to determine this
98
+ """
99
+ lagna = self.master.lagna()
100
+ signs_from_lagna = self.rashis_from_lagna()
101
+ match signs_from_lagna:
102
+ case 1 | 4 | 7 | 10:
103
+ return "Angle"
104
+ case 2 | 5 | 8 | 11:
105
+ return "Panaphara"
106
+ case 3 | 6 | 9 | 12:
107
+ return "Apoklima"
108
+
109
+ def karakas(self) -> [Planet]:
110
+ """
111
+ return a list of Planet classes of the karakas in this sign
112
+ karakas are sun-sat
113
+ """
114
+ ks = []
115
+ for planet in self.planets():
116
+ if planet.is_karaka():
117
+ ks.append(planet)
118
+ return ks
119
+
120
+ def grahas(self) -> [Planet]:
121
+ """
122
+ return a list of Planet classes of the grahas in this sign
123
+ grahas are sun-ketu
124
+ """
125
+ gs = []
126
+ for planet in self.planets():
127
+ if planet.is_graha():
128
+ gs.append(planet)
129
+ return gs
130
+
131
+ def how_many_objects(self):
132
+ return len(self._objects)
133
+
134
+ def how_many_karakas(self):
135
+ return len(self.karakas())
136
+
137
+ def how_many_grahas(self):
138
+ return len(self.grahas())
139
+
140
+ def objects(self):
141
+ return self._objects
142
+
143
+ def objects_within_one_degree(self):
144
+ objects = []
145
+ for object_chosen in self.objects():
146
+ for object_line in self.objects():
147
+ if object_chosen == object_line:
148
+ continue
149
+ if abs(object_chosen.amsha_longitude()-object_line.amsha_longitude()) < 1:
150
+ objects.append((object_chosen,object_line))
151
+ return objects
152
+
153
+ def init_objects(self):
154
+ if self.context.sysflg == const.BARY or self.context.sysflg == const.HELIO:
155
+ return self._planets
156
+ else:
157
+ return self._planets + self._cusps
158
+
159
+ def astrological_signs_forward(self,n) -> int:
160
+ """
161
+ go forward n signs
162
+ this means in the astrologically sense
163
+ so this sign is 1 and then we count
164
+ e.g., if this sign is sign 8 and we go forward 4 signs ->
165
+ 8,9,10,11, so 4 signs forwards from Scorpio is Aquarius
166
+ so self.astrological_signs_forward(1) = self
167
+
168
+ but in terms of sign numbers, we add n-1 to the sign number
169
+ and have to deal with how it wraps around
170
+
171
+
172
+ """
173
+ if n == 0 or n == -1:
174
+ return self.sign()
175
+ if n < 0:
176
+ # go backwards
177
+ almost = (self.sign() - (abs(n)-1))%12
178
+ if almost == 0:
179
+ return 12
180
+ else:
181
+ return almost
182
+ forward = self.sign() + (n-1)
183
+ if forward > 0 and forward <= 12:
184
+ return forward
185
+ else:
186
+ return forward % 12
187
+
188
+ def astrological_signs_backward(self, n: int):
189
+ """
190
+ go backwards n signs in the astrological sense
191
+ this just calls astrological_signs_forward with -n
192
+ """
193
+ return self.astrological_signs_forward(-n)
194
+
195
+ def astrological_signs_apart(self, other_sign: int) -> int:
196
+ """
197
+ how many signs apart are this one sign and other_sign
198
+ if self=12 and other=1 -> (1-12)%12 = 1
199
+ astrological means counting in an astrological way
200
+ i.e., signs 10 and 1 are 4 signs apart
201
+ other_sign is the sign number of the other sign
202
+ """
203
+ return ((other_sign - self.sign())%12)+1
204
+
205
+ def modality(self):
206
+ """
207
+ return modality "Moveable", "Fixed", "Dual"
208
+ """
209
+ match self.sign():
210
+ case 1 | 4 | 7 | 10:
211
+ return "Moveable"
212
+ case 2 | 5 | 8 | 11:
213
+ return "Fixed"
214
+ case 3 | 6 | 9 | 12:
215
+ return "Dual"
216
+
217
+ def ordered_cusps(self, reverse=False):
218
+ return sorted(self.cusps(),key = (lambda cusp: cusp.ecliptic_longitude()), reverse=reverse)
219
+
220
+ def ordered_planets(self, reverse=False):
221
+ return sorted(self.planets(),key = (lambda cusp: cusp.ecliptic_longitude()), reverse=reverse)
222
+
223
+ def ordered_objects(self, reverse=False):
224
+ return sorted(self.ordered_cusps() + self.ordered_planets(),key = (lambda obj: obj.amsha_longitude()), reverse=reverse)
225
+
226
+ def __str__(self):
227
+ """
228
+ a string representation that is used for printing charts
229
+ each objects is listed:
230
+ name
231
+ longitude
232
+ """
233
+ ret = ""
234
+
235
+ for obj in self.ordered_objects():
236
+ if not self.context.print_outer_planets and obj.object_type()=="Planet" and obj.is_outer_planet():
237
+ # dont print outer objs
238
+ continue
239
+ if obj.identity() == "Sun" and self.context.sysflg == const.HELIO:
240
+ # dont print sun with heliocentric coordinates
241
+ continue
242
+ if (obj.identity() == "Rahu" or obj.identity() == "Ketu") and (self.context.sysflg == const.HELIO or self.context.sysflg == const.BARY):
243
+ # dont print Rahu or Ketu for helio/barycentric
244
+ continue
245
+ if isinstance(obj,Cusp) and (self.context.sysflg == const.HELIO or self.context.sysflg == const.BARY):
246
+ # dont print cusps in heliocentric or barycentric
247
+ continue
248
+ ret += f"{obj.name()} "
249
+ if self.context.signize:
250
+ ret += f"{obj.longitude().split(" ")[0]}\n" # remove the sign name here since we are printing in a south indian chart
251
+ else:
252
+ ret += f"{obj.raw_longitude()}\n" # remove the sign name here since we are printing in a south indian chart
253
+ if self.context.print_nakshatras and (self.context.sysflg != const.BARY and self.context.sysflg != const.HELIO):
254
+ ret += f"{obj.nakshatra_name()} "
255
+ ret += f"{obj.nakshatra().elapsed()}\n"
256
+ ret += "\n"
257
+
258
+ # remove final \n when returning
259
+ return ret[:-1]
260
+
261
+ def __repr__(self):
262
+ header = ""
263
+ header += f"\n{self.sign()=} {self.sign_name()}\t{self.context.amsha}\n"
264
+ output = PrettyTable()
265
+ output.field_names = ["Object", "In Amsha Longitude", "Ecliptic Longitude"]
266
+ output.align["Object"] = "l"
267
+ output.align["In Sign Longitude"] = "r"
268
+ output.align["Ecliptic Longitude"] = "r"
269
+
270
+ for p in self._planets:
271
+ output.add_row([p.name(),p.amsha_in_sign_longitude(),p.raw_longitude()])
272
+ for c in self._cusps:
273
+ output.add_row([c.name(),c.amsha_in_sign_longitude(),c.raw_longitude()])
274
+
275
+ ret = output.get_string(fields=["Object", "In Amsha Longitude", "Ecliptic Longitude"])
276
+
277
+ return header + ret
278
+
279
+ def richDrawing(self, header_style="#6b00ff", info_style="#00ff00"):
280
+ sign = Table(box=box.ROUNDED, style=header_style)
281
+
282
+ # add a "header_style=" argument to change color of this header itself
283
+ sign.add_column(f"{self.sign()} {self.name()}",justify="center",style=info_style)
284
+
285
+ # add objects in rows
286
+ sign_str = self.__str__()
287
+ lines = sign_str.split("\n")
288
+ for line in lines:
289
+ sign.add_row(line)
290
+
291
+ return sign
292
+
293
+ def rich(self):
294
+ console = Console()
295
+ console.print(self.richDrawing())
296
+
297
+
298
+ class One(Sign):
299
+
300
+ def __init__(self,planets,cusps,context,master):
301
+ super().__init__(1,planets,cusps,context,master)
302
+
303
+ def modality(self) -> str:
304
+ return "Moveable"
305
+
306
+ def element(self):
307
+ return "Fire"
308
+
309
+ def glyph(self):
310
+ return "♈"
311
+
312
+ class Two(Sign):
313
+
314
+ def __init__(self,planets,cusps,context,master):
315
+ super().__init__(2,planets,cusps,context,master)
316
+
317
+ def modality(self) -> str:
318
+ return "Fixed"
319
+
320
+ def element(self):
321
+ return "Earth"
322
+
323
+ def glyph(self):
324
+ return "♉"
325
+
326
+ class Three(Sign):
327
+
328
+ def __init__(self,planets,cusps,context,master):
329
+ super().__init__(3,planets,cusps,context,master)
330
+
331
+ def modality(self) -> str:
332
+ return "Dual"
333
+
334
+ def element(self):
335
+ return "Air"
336
+
337
+ def glyph(self):
338
+ return "♊"
339
+
340
+ class Four(Sign):
341
+
342
+ def __init__(self,planets,cusps,context,master):
343
+ super().__init__(4,planets,cusps,context,master)
344
+
345
+ def modality(self) -> str:
346
+ return "Moveable"
347
+
348
+ def element(self):
349
+ return "Water"
350
+
351
+ def glyph(self):
352
+ return "♋"
353
+
354
+ class Five(Sign):
355
+
356
+ def __init__(self,planets,cusps,context,master):
357
+ super().__init__(5,planets,cusps,context,master)
358
+
359
+ def modality(self) -> str:
360
+ return "Fixed"
361
+
362
+ def element(self):
363
+ return "Fire"
364
+
365
+ def glyph(self):
366
+ return "♌️"
367
+
368
+
369
+ class Six(Sign):
370
+
371
+ def __init__(self,planets,cusps,context,master):
372
+ super().__init__(6,planets,cusps,context,master)
373
+
374
+ def modality(self) -> str:
375
+ return "Dual"
376
+
377
+ def element(self):
378
+ return "Earth"
379
+
380
+ def glyph(self):
381
+ return "♍"
382
+
383
+ class Seven(Sign):
384
+
385
+ def __init__(self,planets,cusps,context,master):
386
+ super().__init__(7,planets,cusps,context,master)
387
+
388
+ def modality(self) -> str:
389
+ return "Moveable"
390
+
391
+ def element(self):
392
+ return "Air"
393
+
394
+ def glyph(self):
395
+ return "♎"
396
+
397
+ class Eight(Sign):
398
+
399
+ def __init__(self,planets,cusps,context,master):
400
+ super().__init__(8,planets,cusps,context,master)
401
+
402
+ def modality(self) -> str:
403
+ return "Fixed"
404
+
405
+ def element(self):
406
+ return "Water"
407
+
408
+ def glyph(self):
409
+ return "♏"
410
+
411
+ class Nine(Sign):
412
+
413
+ def __init__(self,planets,cusps,context,master):
414
+ super().__init__(9,planets,cusps,context,master)
415
+
416
+ def modality(self) -> str:
417
+ return "Dual"
418
+
419
+ def element(self):
420
+ return "Fire"
421
+
422
+ def glyph(self):
423
+ return "♐"
424
+
425
+ class Ten(Sign):
426
+
427
+ def __init__(self,planets,cusps,context,master):
428
+ super().__init__(10,planets,cusps,context,master)
429
+
430
+ def modality(self) -> str:
431
+ return "Moveable"
432
+
433
+ def element(self):
434
+ return "Earth"
435
+
436
+ def glyph(self):
437
+ return "♑"
438
+
439
+ class Eleven(Sign):
440
+
441
+ def __init__(self,planets,cusps,context,master):
442
+ super().__init__(11,planets,cusps,context,master)
443
+
444
+ def modality(self) -> str:
445
+ return "Fixed"
446
+
447
+ def element(self):
448
+ return "Air"
449
+
450
+ def glyph(self):
451
+ return "♒"
452
+
453
+ class Twelve(Sign):
454
+
455
+ def __init__(self,planets,cusps,context,master):
456
+ super().__init__(12,planets,cusps,context,master)
457
+
458
+ def modality(self) -> str:
459
+ return "Dual"
460
+
461
+ def element(self):
462
+ return "Water"
463
+
464
+ def glyph(self):
465
+ return "♓"
466
+
467
+
468
+ local_Signs = {
469
+ 1: One,
470
+ 2: Two,
471
+ 3: Three,
472
+ 4: Four,
473
+ 5: Five,
474
+ 6: Six,
475
+ 7: Seven,
476
+ 8: Eight,
477
+ 9: Nine,
478
+ 10: Ten,
479
+ 11: Eleven,
480
+ 12: Twelve
481
+ }
482
+
483
+ class Signs:
484
+
485
+ def __init__(self, planets=Planets(), cusps=Cusps(), context=EphContext()):
486
+ self.context = context
487
+ self._planets = planets
488
+ self._cusps = cusps
489
+ self.circle = self.context.circle
490
+ self.aspects = const.rashi_aspects[self.context.rashi_aspects]
491
+ self._signs = self.init_Signs()
492
+ self.sysflgstr = const.sysflgstr(context.sysflg)
493
+
494
+ def __iter__(self):
495
+ return iter(self._signs.values())
496
+
497
+ def __getitem__(self,n: int) -> Self:
498
+ """
499
+ s=Signs(), then you can write
500
+ then you can write s[key] with key between 1 and 12 inclusive
501
+ modified so you can use negatives to go backwards
502
+ astrologal then means that
503
+ """
504
+
505
+ return self._signs[n]
506
+
507
+ def keys(self):
508
+ """
509
+ say s=Signs()
510
+ without this method, i have to do s.signs().keys()
511
+ s.signs() is the dictionary of signs
512
+ with this, i can just write s.keys()
513
+ """
514
+ return self._signs.keys()
515
+
516
+ def amsha(self):
517
+ return self.context.amsha
518
+
519
+ def __str__(self):
520
+ ret = ""
521
+ ret += self.mkheader()
522
+
523
+ for number,sign in self._signs.items():
524
+ ret += f"{number}: {sign}\n"
525
+ ret += "\n"
526
+
527
+ return ret
528
+
529
+ def init_Signs(self):
530
+ """
531
+ initialize signs
532
+ first go through all the planets and cusps
533
+ and put them in the right holding place in "signs", then
534
+ initialize a list of Sign classes
535
+ """
536
+ stmp = [[[],[]] for x in range(0,12)]
537
+ # do planets first, inner index = 0
538
+ inner_index = 0
539
+ for p in self._planets:
540
+ stmp[p.amsha_sign_index()][inner_index].append(p)
541
+ # now do cusps
542
+ inner_index = 1
543
+ for c in self._cusps:
544
+ stmp[c.amsha_sign_index()][inner_index].append(c)
545
+ retsigns={}
546
+ for n, sign in enumerate(stmp):
547
+ retsigns[n+1] = local_Signs[n+1](planets=sign[0],cusps=sign[1],context=self.context,master=self)
548
+ return retsigns
549
+
550
+ def signs(self):
551
+ return self._signs
552
+
553
+ def rashi_aspect_between(self, sign1: Sign, sign2: Sign):
554
+ """
555
+ tells if there are rashi aspects between sign1 and sign2
556
+ 0 - none either way
557
+ 1 - sign1 aspects sign2
558
+ 2 - sign2 aspects sign1
559
+ 3 - both aspect each other
560
+ i.e., sign1 aspects means that there is at least one graha in sign1 caring its aspect to sign2
561
+ """
562
+ ltr = self.rashi_aspect_from_to(sign1,sign2)
563
+ rtl = self.rashi_aspect_from_to(sign2,sign1)
564
+ match (ltr,rtl):
565
+ case (0,0):
566
+ return 0
567
+ case (1,0):
568
+ return 1
569
+ case (0,1):
570
+ return 2
571
+ case (1,1):
572
+ return 3
573
+
574
+ def rashi_aspect_from_to(self, sign1: Sign, sign2: Sign):
575
+ """
576
+ tells if there are rashi aspects between sign1 and sign2
577
+ 0 - none either way
578
+ 1 - sign1 aspects sign2
579
+ i.e., sign1 aspects means that there is at least one graha in sign1 caring its aspect to sign2
580
+ """
581
+ if not (sign2.sign() in self.aspects[sign1.sign()]):
582
+ # sign1 is the key to the aspects dictionary
583
+ # the value is a tuple of integers, the signs which sign1 aspects
584
+ # if sign2 is one of those signs, then sign1 can aspect, depending
585
+ # on planets in a sign
586
+ # so if not, return 0, meaning no aspect
587
+ return 0
588
+ # else: sign1 can aspect sign2; if sign1 does have a graha, it aspects, so return 1
589
+ # needs to be grahas; not .planets(), that includes Uranus, etc.
590
+ if sign1.grahas():
591
+ return 1
592
+ else:
593
+ # 0 means there is no aspect. there could be if there were a planet, but there isnt so no planet
594
+ return 0
595
+
596
+ def rashi_aspects_given_by(self, sign: Sign) -> [Sign]:
597
+ """
598
+ get Signs that rashi aspect Sign sign
599
+ """
600
+ # sign aspects 3 signs
601
+ # those 3 signs aspect this sign
602
+ # for practical purposes, we want to know which of these 3 signs has grahas in them
603
+ # for this purpose, only the Sign-s of those three that grahas are returned
604
+ aspected_signs = self.aspects[sign.sign()]
605
+ ret = []
606
+ for aspected_sign in [self.signs()[each] for each in aspected_signs]:
607
+ if self.rashi_aspect_from_to(aspected_sign,sign):
608
+ ret.append(aspected_sign)
609
+ return ret
610
+
611
+ def rashi_aspects_given_to(self, sign: Sign) -> [Sign]:
612
+ """
613
+ get Signs that rashi aspect Sign sign
614
+ """
615
+ # sign aspects 3 signs
616
+ # those 3 signs aspect this sign
617
+ # for practical purposes, we want to know which of these 3 signs has grahas in them
618
+ # for this purpose, only the Sign-s of those three that grahas are returned
619
+ aspecting_signs = self.aspects[sign.sign()]
620
+ ret = []
621
+ for aspecting_sign in [self.signs()[each] for each in aspecting_signs]:
622
+ if self.rashi_aspect_from_to(aspecting_sign,sign):
623
+ ret.append(aspecting_sign)
624
+ return ret
625
+
626
+ def lagna(self) -> Sign:
627
+ """
628
+ return the Sign class of the lagna sign, i.e., whichever one has Cusp 1
629
+ """
630
+ return self.where_is(1) # 1 means Cusp 1
631
+
632
+ def where_is(self, object: int | str) -> Sign:
633
+ """
634
+ if object is int, a Cusp
635
+ if object is str, a Planet id
636
+ return the Sign class that has in it object
637
+ """
638
+ if isinstance(object, Planet):
639
+ object = object.identity()
640
+ if isinstance(object, Cusp):
641
+ object = object.number()
642
+ for sign in self:
643
+ if isinstance(object, str):
644
+ for planet in sign.planets():
645
+ if object == planet.identity():
646
+ return sign
647
+ if isinstance(object, int):
648
+ for cusp in sign.cusps():
649
+ if object == cusp.number():
650
+ return sign
651
+
652
+ def get_signs_of(self, planet: Planet | str) -> [Sign]:
653
+ """
654
+ get the signs of Planet, either a Planet class or a Planet.identity
655
+ """
656
+ ret = []
657
+ # find the lord string of the Planet
658
+ if isinstance(planet, Planet):
659
+ planet = planet.identity()
660
+ for sign in self:
661
+ if sign.lord() == planet:
662
+ ret.append(sign)
663
+ return ret
664
+
665
+ def most_objects(self):
666
+ """
667
+ return the number of objects that is greatest of any sign
668
+ """
669
+ most=0
670
+ for sign in self:
671
+ if sign.how_many_objects() > most:
672
+ most = sign.how_many_objects()
673
+ return most
674
+
675
+ def mkheader(self):
676
+ return utils.mkheader(self)
677
+
678
+ # this is to placate a field in utils.mkheader()
679
+ # this information is in Varga and is not really necessary
680
+ # since we do know the amsha
681
+ def varga_name(self):
682
+ return ""
683
+
684
+ def __repr__(self):
685
+ """
686
+ represents as a header with the chart information
687
+ """
688
+ return self.mkheader()
689
+
690
+