adnus 0.1.9__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.
adnus/main.py ADDED
@@ -0,0 +1,1804 @@
1
+ """
2
+ adnus (AdNuS): A Python library for Advanced Number Systems.
3
+ Unified interface for hypercomplex numbers, neutrosophic numbers, and other advanced number systems.
4
+ """
5
+
6
+ from __future__ import annotations
7
+ from abc import ABC, abstractmethod
8
+ from dataclasses import dataclass
9
+ from fractions import Fraction
10
+ import math
11
+ from math import sqrt
12
+ from typing import List, Union, Generator, Tuple, Any, Optional
13
+ import numpy as np
14
+ import warnings
15
+
16
+ # Try to import kececinumbers, but provide fallbacks
17
+ try:
18
+ from kececinumbers import (
19
+ reals, Complex as KComplex, Quaternion as KQuaternion,
20
+ Octonion as KOctonion, Sedenion as KSedenion,
21
+ Pathion as KPathion, Chingon as KChingon,
22
+ Routon as KRouton, Voudon as KVoudon,
23
+ cayley_dickson_process, cayley_dickson_cebir
24
+ )
25
+ HAS_KECECI = True
26
+ except ImportError:
27
+ HAS_KECECI = False
28
+ # generate dummy classes for type hints
29
+ class KComplex: pass
30
+ class KQuaternion: pass
31
+ class KOctonion: pass
32
+ class KSedenion: pass
33
+ class KPathion: pass
34
+ class KChingon: pass
35
+ class KRouton: pass
36
+ class KVoudon: pass
37
+
38
+
39
+ # =============================================
40
+ # Abstract Base Class
41
+ # =============================================
42
+
43
+ class AdvancedNumber(ABC):
44
+ """Abstract Base Class for all advanced number systems."""
45
+
46
+ @abstractmethod
47
+ def __add__(self, other):
48
+ pass
49
+
50
+ @abstractmethod
51
+ def __sub__(self, other):
52
+ pass
53
+
54
+ @abstractmethod
55
+ def __mul__(self, other):
56
+ pass
57
+
58
+ @abstractmethod
59
+ def __truediv__(self, other):
60
+ pass
61
+
62
+ @abstractmethod
63
+ def __eq__(self, other) -> bool:
64
+ pass
65
+
66
+ @abstractmethod
67
+ def __repr__(self) -> str:
68
+ pass
69
+
70
+ @abstractmethod
71
+ def norm(self) -> float:
72
+ """Return the Euclidean norm/magnitude."""
73
+ pass
74
+
75
+ @abstractmethod
76
+ def conjugate(self):
77
+ """Return the conjugate."""
78
+ pass
79
+
80
+ @abstractmethod
81
+ def to_hypercomplex(self) -> 'HypercomplexNumber':
82
+ """Convert to HypercomplexNumber."""
83
+ pass
84
+
85
+ # =============================================
86
+ # Complex Number Implementation
87
+ # =============================================
88
+
89
+ class ComplexNumber(AdvancedNumber):
90
+ """Complex number implementation."""
91
+
92
+ def __init__(self, real: float, imag: float = 0.0):
93
+ self._real = float(real)
94
+ self._imag = float(imag)
95
+
96
+ @property
97
+ def real(self) -> float:
98
+ return self._real
99
+
100
+ @property
101
+ def imag(self) -> float:
102
+ return self._imag
103
+
104
+ def __add__(self, other):
105
+ if isinstance(other, ComplexNumber):
106
+ return ComplexNumber(self.real + other.real, self.imag + other.imag)
107
+ elif isinstance(other, (int, float)):
108
+ return ComplexNumber(self.real + float(other), self.imag)
109
+ elif isinstance(other, complex):
110
+ return ComplexNumber(self.real + other.real, self.imag + other.imag)
111
+ return NotImplemented
112
+
113
+ def __radd__(self, other):
114
+ return self.__add__(other)
115
+
116
+ def __sub__(self, other):
117
+ if isinstance(other, ComplexNumber):
118
+ return ComplexNumber(self.real - other.real, self.imag - other.imag)
119
+ elif isinstance(other, (int, float)):
120
+ return ComplexNumber(self.real - float(other), self.imag)
121
+ elif isinstance(other, complex):
122
+ return ComplexNumber(self.real - other.real, self.imag - other.imag)
123
+ return NotImplemented
124
+
125
+ def __rsub__(self, other):
126
+ if isinstance(other, (int, float)):
127
+ return ComplexNumber(float(other) - self.real, -self.imag)
128
+ elif isinstance(other, complex):
129
+ return ComplexNumber(other.real - self.real, other.imag - self.imag)
130
+ return NotImplemented
131
+
132
+ def __mul__(self, other):
133
+ if isinstance(other, ComplexNumber):
134
+ real = self.real * other.real - self.imag * other.imag
135
+ imag = self.real * other.imag + self.imag * other.real
136
+ return ComplexNumber(real, imag)
137
+ elif isinstance(other, (int, float)):
138
+ return ComplexNumber(self.real * float(other), self.imag * float(other))
139
+ elif isinstance(other, complex):
140
+ return self * ComplexNumber(other.real, other.imag)
141
+ return NotImplemented
142
+
143
+ def __rmul__(self, other):
144
+ return self.__mul__(other)
145
+
146
+ def __truediv__(self, other):
147
+ if isinstance(other, ComplexNumber):
148
+ denominator = other.norm() ** 2
149
+ if denominator == 0:
150
+ raise ZeroDivisionError("Division by zero")
151
+ conj = other.conjugate()
152
+ result = self * conj
153
+ return ComplexNumber(result.real / denominator, result.imag / denominator)
154
+ elif isinstance(other, (int, float)):
155
+ if float(other) == 0:
156
+ raise ZeroDivisionError("Division by zero")
157
+ return ComplexNumber(self.real / float(other), self.imag / float(other))
158
+ elif isinstance(other, complex):
159
+ return self / ComplexNumber(other.real, other.imag)
160
+ return NotImplemented
161
+
162
+ def __rtruediv__(self, other):
163
+ if isinstance(other, (int, float)):
164
+ return ComplexNumber(float(other), 0) / self
165
+ elif isinstance(other, complex):
166
+ return ComplexNumber(other.real, other.imag) / self
167
+ return NotImplemented
168
+
169
+ def __neg__(self):
170
+ return ComplexNumber(-self.real, -self.imag)
171
+
172
+ def __pos__(self):
173
+ return self
174
+
175
+ def __abs__(self):
176
+ return self.norm()
177
+
178
+ def __eq__(self, other):
179
+ if isinstance(other, ComplexNumber):
180
+ return math.isclose(self.real, other.real) and math.isclose(self.imag, other.imag)
181
+ elif isinstance(other, (int, float)):
182
+ return math.isclose(self.real, float(other)) and math.isclose(self.imag, 0)
183
+ elif isinstance(other, complex):
184
+ return math.isclose(self.real, other.real) and math.isclose(self.imag, other.imag)
185
+ return False
186
+
187
+ def __ne__(self, other):
188
+ return not self.__eq__(other)
189
+
190
+ def __hash__(self):
191
+ return hash((round(self.real, 12), round(self.imag, 12)))
192
+
193
+ def __repr__(self):
194
+ return f"ComplexNumber({self.real}, {self.imag})"
195
+
196
+ def __str__(self):
197
+ if self.imag >= 0:
198
+ return f"{self.real} + {self.imag}i"
199
+ else:
200
+ return f"{self.real} - {-self.imag}i"
201
+
202
+ def norm(self) -> float:
203
+ return math.sqrt(self.real**2 + self.imag**2)
204
+
205
+ def conjugate(self):
206
+ return ComplexNumber(self.real, -self.imag)
207
+
208
+ def to_complex(self) -> complex:
209
+ return complex(self.real, self.imag)
210
+
211
+ # =============================================
212
+ # Bicomplex Number (uses HypercomplexNumber for components)
213
+ # =============================================
214
+
215
+ @dataclass(frozen=True)
216
+ class BicomplexNumber(AdvancedNumber):
217
+ """
218
+ Bicomplex number: z = z1 + z2·j where z1, z2 ∈ ℂ and j² = -1 (independent imaginary unit).
219
+ """
220
+ z1: HypercomplexNumber # First complex component
221
+ z2: HypercomplexNumber # Second complex component
222
+
223
+ def __post_init__(self):
224
+ # Convert to HypercomplexNumber if needed
225
+ # DÜZELTME: Eğer z1 veya z2 HypercomplexNumber değilse, dönüştür
226
+ if not isinstance(self.z1, HypercomplexNumber):
227
+ if isinstance(self.z1, complex):
228
+ object.__setattr__(self, 'z1', HypercomplexNumber(self.z1.real, self.z1.imag, dimension=2))
229
+ elif isinstance(self.z1, (int, float)):
230
+ object.__setattr__(self, 'z1', HypercomplexNumber(float(self.z1), 0.0, dimension=2))
231
+ elif isinstance(self.z1, ComplexNumber): # Eğer ComplexNumber sınıfı varsa
232
+ object.__setattr__(self, 'z1', HypercomplexNumber(self.z1.real, self.z1.imag, dimension=2))
233
+ else:
234
+ # Diğer türler için from_any kullan
235
+ object.__setattr__(self, 'z1', HypercomplexNumber.from_any(self.z1).pad_to_dimension(2))
236
+
237
+ if not isinstance(self.z2, HypercomplexNumber):
238
+ if isinstance(self.z2, complex):
239
+ object.__setattr__(self, 'z2', HypercomplexNumber(self.z2.real, self.z2.imag, dimension=2))
240
+ elif isinstance(self.z2, (int, float)):
241
+ object.__setattr__(self, 'z2', HypercomplexNumber(float(self.z2), 0.0, dimension=2))
242
+ elif isinstance(self.z2, ComplexNumber): # Eğer ComplexNumber sınıfı varsa
243
+ object.__setattr__(self, 'z2', HypercomplexNumber(self.z2.real, self.z2.imag, dimension=2))
244
+ else:
245
+ # Diğer türler için from_any kullan
246
+ object.__setattr__(self, 'z2', HypercomplexNumber.from_any(self.z2).pad_to_dimension(2))
247
+
248
+ # Ensure both are complex numbers (dimension 2)
249
+ # DÜZELTME: dimension attribute kontrolü
250
+ if hasattr(self.z1, 'dimension') and self.z1.dimension != 2:
251
+ object.__setattr__(self, 'z1', self.z1.pad_to_dimension(2))
252
+
253
+ if hasattr(self.z2, 'dimension') and self.z2.dimension != 2:
254
+ object.__setattr__(self, 'z2', self.z2.pad_to_dimension(2))
255
+
256
+ def __add__(self, other):
257
+ if isinstance(other, BicomplexNumber):
258
+ return BicomplexNumber(self.z1 + other.z1, self.z2 + other.z2)
259
+ else:
260
+ # Try to convert to HypercomplexNumber first
261
+ try:
262
+ other_h = HypercomplexNumber.from_any(other)
263
+ if other_h.dimension == 2:
264
+ return BicomplexNumber(self.z1 + other_h, self.z2)
265
+ else:
266
+ # If higher dimension, convert to HypercomplexNumber
267
+ return self.to_hypercomplex() + other_h
268
+ except:
269
+ return NotImplemented
270
+
271
+ def __radd__(self, other):
272
+ return self.__add__(other)
273
+
274
+ def __sub__(self, other):
275
+ if isinstance(other, BicomplexNumber):
276
+ return BicomplexNumber(self.z1 - other.z1, self.z2 - other.z2)
277
+ else:
278
+ try:
279
+ other_h = HypercomplexNumber.from_any(other)
280
+ if other_h.dimension == 2:
281
+ return BicomplexNumber(self.z1 - other_h, self.z2)
282
+ else:
283
+ return self.to_hypercomplex() - other_h
284
+ except:
285
+ return NotImplemented
286
+
287
+ def __rsub__(self, other):
288
+ try:
289
+ other_h = HypercomplexNumber.from_any(other)
290
+ if other_h.dimension == 2:
291
+ return BicomplexNumber(other_h - self.z1, -self.z2)
292
+ else:
293
+ return other_h - self.to_hypercomplex()
294
+ except:
295
+ return NotImplemented
296
+
297
+ def __mul__(self, other):
298
+ if isinstance(other, BicomplexNumber):
299
+ # (z1 + z2j)(w1 + w2j) = (z1w1 - z2w2) + (z1w2 + z2w1)j
300
+ z1w1 = self.z1 * other.z1
301
+ z2w2 = self.z2 * other.z2
302
+ z1w2 = self.z1 * other.z2
303
+ z2w1 = self.z2 * other.z1
304
+
305
+ return BicomplexNumber(z1w1 - z2w2, z1w2 + z2w1)
306
+ else:
307
+ try:
308
+ other_h = HypercomplexNumber.from_any(other)
309
+ if other_h.dimension == 2:
310
+ return BicomplexNumber(self.z1 * other_h, self.z2 * other_h)
311
+ else:
312
+ return self.to_hypercomplex() * other_h
313
+ except:
314
+ return NotImplemented
315
+
316
+ def __rmul__(self, other):
317
+ return self.__mul__(other)
318
+
319
+ def __truediv__(self, other):
320
+ if isinstance(other, BicomplexNumber):
321
+ # Use conjugate method: a/b = a * conj(b) / |b|²
322
+ conj = other.conjugate()
323
+ numerator = self * conj
324
+ denominator = other.norm() ** 2
325
+ if denominator == 0:
326
+ raise ZeroDivisionError("Division by zero bicomplex")
327
+ return BicomplexNumber(numerator.z1 / denominator, numerator.z2 / denominator)
328
+ else:
329
+ try:
330
+ other_h = HypercomplexNumber.from_any(other)
331
+ if other == 0:
332
+ raise ZeroDivisionError("Division by zero")
333
+ return BicomplexNumber(self.z1 / other_h, self.z2 / other_h)
334
+ except:
335
+ return NotImplemented
336
+
337
+ def __rtruediv__(self, other):
338
+ try:
339
+ other_h = HypercomplexNumber.from_any(other)
340
+ if other_h.dimension == 2:
341
+ return BicomplexNumber(other_h, HypercomplexNumber(0, 0, dimension=2)) / self
342
+ else:
343
+ return other_h / self.to_hypercomplex()
344
+ except:
345
+ return NotImplemented
346
+
347
+ def __neg__(self):
348
+ return BicomplexNumber(-self.z1, -self.z2)
349
+
350
+ def __pos__(self):
351
+ return self
352
+
353
+ def __abs__(self):
354
+ return self.norm()
355
+
356
+ def __eq__(self, other):
357
+ if isinstance(other, BicomplexNumber):
358
+ return self.z1 == other.z1 and self.z2 == other.z2
359
+ else:
360
+ try:
361
+ other_h = HypercomplexNumber.from_any(other)
362
+ if other_h.dimension == 2:
363
+ return self.z1 == other_h and self.z2 == HypercomplexNumber(0, 0, dimension=2)
364
+ else:
365
+ return self.to_hypercomplex() == other_h
366
+ except:
367
+ return False
368
+
369
+ def __ne__(self, other):
370
+ return not self.__eq__(other)
371
+
372
+ def __hash__(self):
373
+ return hash((self.z1, self.z2))
374
+
375
+ def __repr__(self):
376
+ return f"BicomplexNumber(z1={self.z1}, z2={self.z2})"
377
+
378
+ def __str__(self):
379
+ return f"({self.z1}) + ({self.z2})·j"
380
+
381
+ def norm(self) -> float:
382
+ return math.sqrt(self.z1.norm()**2 + self.z2.norm()**2)
383
+
384
+ def conjugate(self):
385
+ return BicomplexNumber(self.z1.conjugate(), -self.z2)
386
+
387
+ def components(self) -> Tuple[float, float, float, float]:
388
+ """Return (Re(z1), Im(z1), Re(z2), Im(z2))."""
389
+ return (self.z1[0], self.z1[1], self.z2[0], self.z2[1])
390
+
391
+ def to_hypercomplex(self) -> HypercomplexNumber:
392
+ """Convert to 4D HypercomplexNumber."""
393
+ return HypercomplexNumber(
394
+ self.z1[0], self.z1[1],
395
+ self.z2[0], self.z2[1],
396
+ dimension=4
397
+ )
398
+
399
+
400
+
401
+ # =============================================
402
+ # Neutrosophic Number
403
+ # =============================================
404
+
405
+ @dataclass(frozen=True)
406
+ class NeutrosophicNumber(AdvancedNumber):
407
+ """
408
+ Neutrosophic number: a + bI where I² = I (indeterminacy).
409
+ """
410
+ determinate: float
411
+ indeterminate: float
412
+
413
+ def __add__(self, other):
414
+ if isinstance(other, NeutrosophicNumber):
415
+ return NeutrosophicNumber(
416
+ self.determinate + other.determinate,
417
+ self.indeterminate + other.indeterminate
418
+ )
419
+ elif isinstance(other, (int, float)):
420
+ return NeutrosophicNumber(self.determinate + other, self.indeterminate)
421
+ return NotImplemented
422
+
423
+ def __radd__(self, other):
424
+ return self.__add__(other)
425
+
426
+ def __sub__(self, other):
427
+ if isinstance(other, NeutrosophicNumber):
428
+ return NeutrosophicNumber(
429
+ self.determinate - other.determinate,
430
+ self.indeterminate - other.indeterminate
431
+ )
432
+ elif isinstance(other, (int, float)):
433
+ return NeutrosophicNumber(self.determinate - other, self.indeterminate)
434
+ return NotImplemented
435
+
436
+ def __rsub__(self, other):
437
+ if isinstance(other, (int, float)):
438
+ return NeutrosophicNumber(other - self.determinate, -self.indeterminate)
439
+ return NotImplemented
440
+
441
+ def __mul__(self, other):
442
+ if isinstance(other, NeutrosophicNumber):
443
+ # (a + bI)(c + dI) = ac + (ad + bc + bd)I
444
+ determinate = self.determinate * other.determinate
445
+ indeterminate = (self.determinate * other.indeterminate +
446
+ self.indeterminate * other.determinate +
447
+ self.indeterminate * other.indeterminate)
448
+ return NeutrosophicNumber(determinate, indeterminate)
449
+ elif isinstance(other, (int, float)):
450
+ return NeutrosophicNumber(
451
+ self.determinate * other,
452
+ self.indeterminate * other
453
+ )
454
+ return NotImplemented
455
+
456
+ def __rmul__(self, other):
457
+ return self.__mul__(other)
458
+
459
+ def __truediv__(self, other):
460
+ if isinstance(other, (int, float)):
461
+ if other == 0:
462
+ raise ZeroDivisionError("Division by zero")
463
+ return NeutrosophicNumber(
464
+ self.determinate / other,
465
+ self.indeterminate / other
466
+ )
467
+ elif isinstance(other, NeutrosophicNumber):
468
+ # Use conjugate: (a + bI)/(c + dI) = (a + bI)(c - dI) / (c² + (c-d)d)
469
+ conj = other.conjugate()
470
+ numerator = self * conj
471
+ denominator = other.determinate**2 + (other.determinate - other.indeterminate) * other.indeterminate
472
+ if denominator == 0:
473
+ raise ZeroDivisionError("Division by zero neutrosophic")
474
+ return NeutrosophicNumber(
475
+ numerator.determinate / denominator,
476
+ numerator.indeterminate / denominator
477
+ )
478
+ return NotImplemented
479
+
480
+ def __rtruediv__(self, other):
481
+ if isinstance(other, (int, float)):
482
+ return NeutrosophicNumber(other, 0) / self
483
+ return NotImplemented
484
+
485
+ def __neg__(self):
486
+ return NeutrosophicNumber(-self.determinate, -self.indeterminate)
487
+
488
+ def __pos__(self):
489
+ return self
490
+
491
+ def __abs__(self):
492
+ return self.norm()
493
+
494
+ def __eq__(self, other):
495
+ if isinstance(other, NeutrosophicNumber):
496
+ return (math.isclose(self.determinate, other.determinate) and
497
+ math.isclose(self.indeterminate, other.indeterminate))
498
+ elif isinstance(other, (int, float)):
499
+ return math.isclose(self.determinate, other) and math.isclose(self.indeterminate, 0)
500
+ return False
501
+
502
+ def __ne__(self, other):
503
+ return not self.__eq__(other)
504
+
505
+ def __hash__(self):
506
+ return hash((round(self.determinate, 12), round(self.indeterminate, 12)))
507
+
508
+ def __repr__(self):
509
+ return f"NeutrosophicNumber({self.determinate}, {self.indeterminate})"
510
+
511
+ def __str__(self):
512
+ if self.indeterminate >= 0:
513
+ return f"{self.determinate} + {self.indeterminate}I"
514
+ else:
515
+ return f"{self.determinate} - {-self.indeterminate}I"
516
+
517
+ def norm(self) -> float:
518
+ return math.sqrt(self.determinate**2 + self.indeterminate**2)
519
+
520
+ def conjugate(self):
521
+ return NeutrosophicNumber(self.determinate, -self.indeterminate)
522
+
523
+ # =============================================
524
+ # Unified Number System: HypercomplexNumber
525
+ # =============================================
526
+
527
+ class HypercomplexNumber(AdvancedNumber):
528
+ """
529
+ Unified hypercomplex number implementation for all dimensions.
530
+ Supports: Real (1), Complex (2), Quaternion (4), Octonion (8), etc.
531
+ """
532
+
533
+ # Dimension names
534
+ DIM_NAMES = {
535
+ 1: "Real",
536
+ 2: "Complex",
537
+ 4: "Quaternion",
538
+ 8: "Octonion",
539
+ 16: "Sedenion",
540
+ 32: "Pathion",
541
+ 64: "Chingon",
542
+ 128: "Routon",
543
+ 256: "Voudon"
544
+ }
545
+
546
+ def __init__(self, *components: float, dimension: Optional[int] = None):
547
+ """
548
+ Initialize hypercomplex number.
549
+
550
+ Args:
551
+ *components: Number components
552
+ dimension: Dimension (power of 2). If None, inferred from components.
553
+ """
554
+ if dimension is None:
555
+ # Find smallest power of 2 >= len(components)
556
+ n = len(components)
557
+ dimension = 1
558
+ while dimension < n and dimension < 256:
559
+ dimension <<= 1
560
+
561
+ if dimension & (dimension - 1) != 0 or dimension < 1:
562
+ raise ValueError(f"Dimension must be power of 2 (1-256), got {dimension}")
563
+
564
+ self.dimension = dimension
565
+
566
+ # Pad or truncate components
567
+ if len(components) < dimension:
568
+ self.coeffs = list(components) + [0.0] * (dimension - len(components))
569
+ elif len(components) > dimension:
570
+ self.coeffs = components[:dimension]
571
+ else:
572
+ self.coeffs = list(components)
573
+
574
+ # Type name - DÜZELTİLDİ: 'dim' yerine 'dimension' kullan
575
+ self.type_name = self.DIM_NAMES.get(dimension, f"Hypercomplex{dimension}")
576
+
577
+ @classmethod
578
+ def from_real(cls, value: float) -> 'HypercomplexNumber':
579
+ """Create from a real number."""
580
+ return cls(value, dimension=1)
581
+
582
+ @classmethod
583
+ def from_complex(cls, real: float, imag: float) -> 'HypercomplexNumber':
584
+ """Create from complex components."""
585
+ return cls(real, imag, dimension=2)
586
+
587
+ @classmethod
588
+ def from_quaternion(cls, w: float, x: float, y: float, z: float) -> 'HypercomplexNumber':
589
+ """Create from quaternion components."""
590
+ return cls(w, x, y, z, dimension=4)
591
+
592
+ @classmethod
593
+ def from_octonion(cls, *coeffs: float) -> 'HypercomplexNumber':
594
+ """Create from octonion components."""
595
+ if len(coeffs) != 8:
596
+ coeffs = list(coeffs) + [0.0] * (8 - len(coeffs))
597
+ return cls(*coeffs, dimension=8)
598
+
599
+ @classmethod
600
+ def from_any(cls, value: Any) -> 'HypercomplexNumber':
601
+ """Create from any numeric type."""
602
+ if isinstance(value, HypercomplexNumber):
603
+ return value
604
+ elif isinstance(value, (int, float)):
605
+ return cls.from_real(float(value))
606
+ elif isinstance(value, complex):
607
+ return cls.from_complex(value.real, value.imag)
608
+ elif isinstance(value, (list, tuple)):
609
+ return cls(*value)
610
+ else:
611
+ try:
612
+ return cls.from_real(float(value))
613
+ except:
614
+ raise ValueError(f"Cannot convert {type(value)} to HypercomplexNumber")
615
+
616
+ @property
617
+ def real(self) -> float:
618
+ """Real part (first component)."""
619
+ return self.coeffs[0]
620
+
621
+ @property
622
+ def imag(self) -> float:
623
+ """Imaginary part (for complex numbers)."""
624
+ if self.dimension >= 2:
625
+ return self.coeffs[1]
626
+ return 0.0
627
+
628
+ def __len__(self):
629
+ return self.dimension
630
+
631
+ def __getitem__(self, idx):
632
+ return self.coeffs[idx]
633
+
634
+ def __iter__(self):
635
+ return iter(self.coeffs)
636
+
637
+ def __add__(self, other):
638
+ # Convert other to HypercomplexNumber if needed
639
+ if not isinstance(other, HypercomplexNumber):
640
+ try:
641
+ other = self.from_any(other)
642
+ except:
643
+ return NotImplemented
644
+
645
+ # Handle different dimensions
646
+ if self.dimension != other.dimension:
647
+ common_dim = max(self.dimension, other.dimension)
648
+ self_padded = self.pad_to_dimension(common_dim)
649
+ other_padded = other.pad_to_dimension(common_dim)
650
+ return self_padded + other_padded
651
+
652
+ new_coeffs = [a + b for a, b in zip(self.coeffs, other.coeffs)]
653
+ return HypercomplexNumber(*new_coeffs, dimension=self.dimension)
654
+
655
+ def __radd__(self, other):
656
+ return self.__add__(other)
657
+
658
+ def __sub__(self, other):
659
+ # Convert other to HypercomplexNumber if needed
660
+ if not isinstance(other, HypercomplexNumber):
661
+ try:
662
+ other = self.from_any(other)
663
+ except:
664
+ return NotImplemented
665
+
666
+ # Handle different dimensions
667
+ if self.dimension != other.dimension:
668
+ common_dim = max(self.dimension, other.dimension)
669
+ self_padded = self.pad_to_dimension(common_dim)
670
+ other_padded = other.pad_to_dimension(common_dim)
671
+ return self_padded - other_padded
672
+
673
+ new_coeffs = [a - b for a, b in zip(self.coeffs, other.coeffs)]
674
+ return HypercomplexNumber(*new_coeffs, dimension=self.dimension)
675
+
676
+ def __rsub__(self, other):
677
+ # Convert other to HypercomplexNumber
678
+ try:
679
+ other_num = self.from_any(other)
680
+ return other_num - self
681
+ except:
682
+ return NotImplemented
683
+
684
+ def _multiply_complex(self, other: 'HypercomplexNumber') -> 'HypercomplexNumber':
685
+ """Complex multiplication (dimension 2)."""
686
+ a, b = self.coeffs
687
+ c, d = other.coeffs
688
+ real = a*c - b*d
689
+ imag = a*d + b*c
690
+ return HypercomplexNumber(real, imag, dimension=2)
691
+
692
+ def _multiply_quaternion(self, other: 'HypercomplexNumber') -> 'HypercomplexNumber':
693
+ """Quaternion multiplication (dimension 4)."""
694
+ w1, x1, y1, z1 = self.coeffs
695
+ w2, x2, y2, z2 = other.coeffs
696
+
697
+ w = w1*w2 - x1*x2 - y1*y2 - z1*z2
698
+ x = w1*x2 + x1*w2 + y1*z2 - z1*y2
699
+ y = w1*y2 - x1*z2 + y1*w2 + z1*x2
700
+ z = w1*z2 + x1*y2 - y1*x2 + z1*w2
701
+
702
+ return HypercomplexNumber(w, x, y, z, dimension=4)
703
+
704
+ def __mul__(self, other):
705
+ # Convert other to HypercomplexNumber if needed
706
+ if not isinstance(other, HypercomplexNumber):
707
+ try:
708
+ other = self.from_any(other)
709
+ except:
710
+ return NotImplemented
711
+
712
+ # Handle different dimensions
713
+ if self.dimension != other.dimension:
714
+ common_dim = max(self.dimension, other.dimension)
715
+ self_padded = self.pad_to_dimension(common_dim)
716
+ other_padded = other.pad_to_dimension(common_dim)
717
+ return self_padded * other_padded
718
+
719
+ # Different multiplication rules based on dimension
720
+ if self.dimension == 1:
721
+ # Real multiplication
722
+ return HypercomplexNumber(self.coeffs[0] * other.coeffs[0], dimension=1)
723
+
724
+ elif self.dimension == 2:
725
+ # Complex multiplication
726
+ return self._multiply_complex(other)
727
+
728
+ elif self.dimension == 4:
729
+ # Quaternion multiplication
730
+ return self._multiply_quaternion(other)
731
+
732
+ else:
733
+ # For higher dimensions, use component-wise as fallback
734
+ warnings.warn(f"Using component-wise multiplication for {self.type_name}", RuntimeWarning)
735
+ new_coeffs = [a * b for a, b in zip(self.coeffs, other.coeffs)]
736
+ return HypercomplexNumber(*new_coeffs, dimension=self.dimension)
737
+
738
+ def __rmul__(self, other):
739
+ return self.__mul__(other)
740
+
741
+ def __truediv__(self, other):
742
+ # Convert other to HypercomplexNumber if needed
743
+ if not isinstance(other, HypercomplexNumber):
744
+ try:
745
+ other = self.from_any(other)
746
+ except:
747
+ return NotImplemented
748
+
749
+ # Use inverse for division
750
+ return self * other.inverse()
751
+
752
+ def __rtruediv__(self, other):
753
+ # Convert other to HypercomplexNumber
754
+ try:
755
+ other_num = self.from_any(other)
756
+ return other_num / self
757
+ except:
758
+ return NotImplemented
759
+
760
+ def __neg__(self):
761
+ new_coeffs = [-c for c in self.coeffs]
762
+ return HypercomplexNumber(*new_coeffs, dimension=self.dimension)
763
+
764
+ def __pos__(self):
765
+ return self
766
+
767
+ def __abs__(self):
768
+ return self.norm()
769
+
770
+ def __eq__(self, other):
771
+ if not isinstance(other, HypercomplexNumber):
772
+ try:
773
+ other = self.from_any(other)
774
+ except:
775
+ return False
776
+
777
+ if self.dimension != other.dimension:
778
+ return False
779
+
780
+ return all(math.isclose(a, b, abs_tol=1e-12) for a, b in zip(self.coeffs, other.coeffs))
781
+
782
+ def __ne__(self, other):
783
+ return not self.__eq__(other)
784
+
785
+ def __hash__(self):
786
+ return hash(tuple(round(c, 12) for c in self.coeffs))
787
+
788
+ def __repr__(self):
789
+ return f"HypercomplexNumber({', '.join(map(str, self.coeffs))}, dimension={self.dimension})"
790
+
791
+ def __str__(self):
792
+ if self.dimension == 1:
793
+ return f"{self.type_name}({self.coeffs[0]})"
794
+ elif self.dimension == 2:
795
+ a, b = self.coeffs
796
+ if b >= 0:
797
+ return f"{self.type_name}({a} + {b}i)"
798
+ else:
799
+ return f"{self.type_name}({a} - {-b}i)"
800
+ elif self.dimension <= 8:
801
+ non_zero = [(i, c) for i, c in enumerate(self.coeffs) if abs(c) > 1e-10]
802
+ if not non_zero:
803
+ return f"{self.type_name}(0)"
804
+
805
+ parts = []
806
+ for i, c in non_zero:
807
+ if i == 0:
808
+ parts.append(f"{c:.4f}")
809
+ else:
810
+ sign = "+" if c >= 0 else "-"
811
+ parts.append(f"{sign} {abs(c):.4f}e{i}")
812
+ return f"{self.type_name}({' '.join(parts)})"
813
+ else:
814
+ return f"{self.type_name}[real={self.real:.4f}, norm={self.norm():.4f}, dim={self.dimension}]"
815
+
816
+ def norm(self) -> float:
817
+ """Euclidean norm."""
818
+ return math.sqrt(sum(c**2 for c in self.coeffs))
819
+
820
+ def conjugate(self):
821
+ """Conjugate (negate all imaginary parts)."""
822
+ if self.dimension == 1:
823
+ return self
824
+
825
+ new_coeffs = self.coeffs.copy()
826
+ for i in range(1, self.dimension):
827
+ new_coeffs[i] = -new_coeffs[i]
828
+
829
+ return HypercomplexNumber(*new_coeffs, dimension=self.dimension)
830
+
831
+ def inverse(self):
832
+ """Multiplicative inverse."""
833
+ norm_sq = self.norm() ** 2
834
+ if norm_sq == 0:
835
+ raise ZeroDivisionError("Cannot invert zero element")
836
+
837
+ conj = self.conjugate()
838
+ new_coeffs = [c / norm_sq for c in conj.coeffs]
839
+ return HypercomplexNumber(*new_coeffs, dimension=self.dimension)
840
+
841
+ def pad_to_dimension(self, new_dim: int) -> 'HypercomplexNumber':
842
+ """Pad to higher dimension with zeros."""
843
+ if new_dim < self.dimension:
844
+ raise ValueError(f"Cannot pad to smaller dimension: {new_dim} < {self.dimension}")
845
+
846
+ if new_dim == self.dimension:
847
+ return self
848
+
849
+ new_coeffs = self.coeffs + [0.0] * (new_dim - self.dimension)
850
+ return HypercomplexNumber(*new_coeffs, dimension=new_dim)
851
+
852
+ def truncate_to_dimension(self, new_dim: int) -> 'HypercomplexNumber':
853
+ """Truncate to smaller dimension."""
854
+ if new_dim > self.dimension:
855
+ raise ValueError(f"Cannot truncate to larger dimension: {new_dim} > {self.dimension}")
856
+
857
+ if new_dim == self.dimension:
858
+ return self
859
+
860
+ new_coeffs = self.coeffs[:new_dim]
861
+ return HypercomplexNumber(*new_coeffs, dimension=new_dim)
862
+
863
+ def to_list(self) -> List[float]:
864
+ """Convert to Python list."""
865
+ return self.coeffs.copy()
866
+
867
+ def to_numpy(self) -> np.ndarray:
868
+ """Convert to numpy array."""
869
+ return np.array(self.coeffs, dtype=np.float64)
870
+
871
+ def copy(self) -> 'HypercomplexNumber':
872
+ """Generate a copy."""
873
+ return HypercomplexNumber(*self.coeffs, dimension=self.dimension)
874
+
875
+ def __float__(self):
876
+ """Convert to float (returns real part)."""
877
+ return float(self.real)
878
+
879
+ def to_complex(self) -> complex:
880
+ """Convert to Python complex if possible."""
881
+ if self.dimension >= 2:
882
+ return complex(self.coeffs[0], self.coeffs[1])
883
+ return complex(self.coeffs[0], 0.0)
884
+
885
+ def to_hypercomplex(self) -> 'HypercomplexNumber':
886
+ """Convert to HypercomplexNumber (identity for this class)."""
887
+ return self
888
+
889
+ # =============================================
890
+ # Neutrosophic Number
891
+ # =============================================
892
+
893
+ @dataclass(frozen=True)
894
+ class NeutrosophicNumber(AdvancedNumber):
895
+ """
896
+ Neutrosophic number: a + bI where I² = I (indeterminacy).
897
+ """
898
+ determinate: float
899
+ indeterminate: float
900
+
901
+ def __add__(self, other):
902
+ if isinstance(other, NeutrosophicNumber):
903
+ return NeutrosophicNumber(
904
+ self.determinate + other.determinate,
905
+ self.indeterminate + other.indeterminate
906
+ )
907
+ elif isinstance(other, (int, float)):
908
+ return NeutrosophicNumber(self.determinate + other, self.indeterminate)
909
+ return NotImplemented
910
+
911
+ def __radd__(self, other):
912
+ return self.__add__(other)
913
+
914
+ def __sub__(self, other):
915
+ if isinstance(other, NeutrosophicNumber):
916
+ return NeutrosophicNumber(
917
+ self.determinate - other.determinate,
918
+ self.indeterminate - other.indeterminate
919
+ )
920
+ elif isinstance(other, (int, float)):
921
+ return NeutrosophicNumber(self.determinate - other, self.indeterminate)
922
+ return NotImplemented
923
+
924
+ def __rsub__(self, other):
925
+ if isinstance(other, (int, float)):
926
+ return NeutrosophicNumber(other - self.determinate, -self.indeterminate)
927
+ return NotImplemented
928
+
929
+ def __mul__(self, other):
930
+ if isinstance(other, NeutrosophicNumber):
931
+ # (a + bI)(c + dI) = ac + (ad + bc + bd)I
932
+ determinate = self.determinate * other.determinate
933
+ indeterminate = (self.determinate * other.indeterminate +
934
+ self.indeterminate * other.determinate +
935
+ self.indeterminate * other.indeterminate)
936
+ return NeutrosophicNumber(determinate, indeterminate)
937
+ elif isinstance(other, (int, float)):
938
+ return NeutrosophicNumber(
939
+ self.determinate * other,
940
+ self.indeterminate * other
941
+ )
942
+ return NotImplemented
943
+
944
+ def __rmul__(self, other):
945
+ return self.__mul__(other)
946
+
947
+ def __truediv__(self, other):
948
+ if isinstance(other, (int, float)):
949
+ if other == 0:
950
+ raise ZeroDivisionError("Division by zero")
951
+ return NeutrosophicNumber(
952
+ self.determinate / other,
953
+ self.indeterminate / other
954
+ )
955
+ elif isinstance(other, NeutrosophicNumber):
956
+ # Use conjugate: (a + bI)/(c + dI) = (a + bI)(c - dI) / (c² + (c-d)d)
957
+ conj = other.conjugate()
958
+ numerator = self * conj
959
+ denominator = other.determinate**2 + (other.determinate - other.indeterminate) * other.indeterminate
960
+ if denominator == 0:
961
+ raise ZeroDivisionError("Division by zero neutrosophic")
962
+ return NeutrosophicNumber(
963
+ numerator.determinate / denominator,
964
+ numerator.indeterminate / denominator
965
+ )
966
+ return NotImplemented
967
+
968
+ def __rtruediv__(self, other):
969
+ if isinstance(other, (int, float)):
970
+ return NeutrosophicNumber(other, 0) / self
971
+ return NotImplemented
972
+
973
+ def __neg__(self):
974
+ return NeutrosophicNumber(-self.determinate, -self.indeterminate)
975
+
976
+ def __pos__(self):
977
+ return self
978
+
979
+ def __abs__(self):
980
+ return self.norm()
981
+
982
+ def __eq__(self, other):
983
+ if isinstance(other, NeutrosophicNumber):
984
+ return (math.isclose(self.determinate, other.determinate) and
985
+ math.isclose(self.indeterminate, other.indeterminate))
986
+ elif isinstance(other, (int, float)):
987
+ return math.isclose(self.determinate, other) and math.isclose(self.indeterminate, 0)
988
+ return False
989
+
990
+ def __ne__(self, other):
991
+ return not self.__eq__(other)
992
+
993
+ def __hash__(self):
994
+ return hash((round(self.determinate, 12), round(self.indeterminate, 12)))
995
+
996
+ def __repr__(self):
997
+ return f"NeutrosophicNumber({self.determinate}, {self.indeterminate})"
998
+
999
+ def __str__(self):
1000
+ if self.indeterminate >= 0:
1001
+ return f"{self.determinate} + {self.indeterminate}I"
1002
+ else:
1003
+ return f"{self.determinate} - {-self.indeterminate}I"
1004
+
1005
+ def norm(self) -> float:
1006
+ return math.sqrt(self.determinate**2 + self.indeterminate**2)
1007
+
1008
+ def conjugate(self):
1009
+ return NeutrosophicNumber(self.determinate, -self.indeterminate)
1010
+
1011
+ def to_hypercomplex(self) -> HypercomplexNumber:
1012
+ """Convert to 2D HypercomplexNumber (treats I as imaginary unit)."""
1013
+ return HypercomplexNumber(self.determinate, self.indeterminate, dimension=2)
1014
+
1015
+
1016
+ # =============================================
1017
+ # Factory Functions (Simplified)
1018
+ # =============================================
1019
+
1020
+ def Real(x: float) -> HypercomplexNumber:
1021
+ """Generate a real number (1D hypercomplex)."""
1022
+ return HypercomplexNumber.from_real(x)
1023
+
1024
+ def Complex(real: float, imag: float) -> HypercomplexNumber:
1025
+ """Generate a complex number (2D hypercomplex)."""
1026
+ return HypercomplexNumber.from_complex(real, imag)
1027
+
1028
+ def Quaternion(w: float, x: float, y: float, z: float) -> HypercomplexNumber:
1029
+ """Generate a quaternion (4D hypercomplex)."""
1030
+ return HypercomplexNumber.from_quaternion(w, x, y, z)
1031
+
1032
+ def Octonion(*coeffs: float) -> HypercomplexNumber:
1033
+ """Generate an octonion (8D hypercomplex)."""
1034
+ return HypercomplexNumber.from_octonion(*coeffs)
1035
+
1036
+ def Bicomplex(z1_real: float, z1_imag: float, z2_real: float, z2_imag: float) -> BicomplexNumber:
1037
+ """Generate a bicomplex number."""
1038
+ z1 = HypercomplexNumber(z1_real, z1_imag, dimension=2)
1039
+ z2 = HypercomplexNumber(z2_real, z2_imag, dimension=2)
1040
+ return BicomplexNumber(z1, z2)
1041
+
1042
+ def Neutrosophic(determinate: float, indeterminate: float) -> NeutrosophicNumber:
1043
+ """Generate a neutrosophic number."""
1044
+ return NeutrosophicNumber(determinate, indeterminate)
1045
+
1046
+
1047
+ def Sedenion(*coeffs) -> HypercomplexNumber:
1048
+ """Generate a sedenion."""
1049
+ if len(coeffs) != 16:
1050
+ coeffs = list(coeffs) + [0.0] * (16 - len(coeffs))
1051
+ return HypercomplexNumber(*coeffs, dimension=16)
1052
+
1053
+ def Pathion(*coeffs) -> HypercomplexNumber:
1054
+ """Generate a pathion."""
1055
+ if len(coeffs) != 32:
1056
+ coeffs = list(coeffs) + [0.0] * (32 - len(coeffs))
1057
+ return HypercomplexNumber(*coeffs, dimension=32)
1058
+
1059
+ def Chingon(*coeffs) -> HypercomplexNumber:
1060
+ """Generate a chingon."""
1061
+ if len(coeffs) != 64:
1062
+ coeffs = list(coeffs) + [0.0] * (64 - len(coeffs))
1063
+ return HypercomplexNumber(*coeffs, dimension=64)
1064
+
1065
+ def Routon(*coeffs) -> HypercomplexNumber:
1066
+ """Generate a routon."""
1067
+ if len(coeffs) != 128:
1068
+ coeffs = list(coeffs) + [0.0] * (128 - len(coeffs))
1069
+ return HypercomplexNumber(*coeffs, dimension=128)
1070
+
1071
+ def Voudon(*coeffs) -> HypercomplexNumber:
1072
+ """Generate a voudon."""
1073
+ if len(coeffs) != 256:
1074
+ coeffs = list(coeffs) + [0.0] * (256 - len(coeffs))
1075
+ return HypercomplexNumber(*coeffs, dimension=256)
1076
+
1077
+ # =============================================
1078
+ # Cayley-Dickson Implementation
1079
+ # =============================================
1080
+
1081
+ def cayley_dickson_process(cebr: type, base_type=float) -> type:
1082
+ """
1083
+ Apply the Cayley-Dickson construction to generate an algebra of twice the dimension.
1084
+ """
1085
+
1086
+ class CayleyDicksonCebr:
1087
+ """Hypercomplex algebra generated via Cayley-Dickson construction."""
1088
+
1089
+ dimensions = None
1090
+ base = base_type
1091
+
1092
+ def __init__(self, *args, pair=False):
1093
+ if pair and len(args) == 2:
1094
+ # (a, b) pair format
1095
+ self.a = self._ensure_cebr(args[0], cebr)
1096
+ self.b = self._ensure_cebr(args[1], cebr)
1097
+ else:
1098
+ # Handle various input formats
1099
+ if len(args) == 1:
1100
+ arg = args[0]
1101
+ # Handle complex numbers
1102
+ if isinstance(arg, complex):
1103
+ # Convert complex to pair (real, imag)
1104
+ self.a = cebr(arg.real)
1105
+ self.b = cebr(arg.imag)
1106
+ return
1107
+ # Handle strings
1108
+ elif isinstance(arg, str):
1109
+ # Try to parse as complex
1110
+ try:
1111
+ c = complex(arg)
1112
+ self.a = cebr(c.real)
1113
+ self.b = cebr(c.imag)
1114
+ return
1115
+ except ValueError:
1116
+ pass
1117
+ # Handle iterables
1118
+ elif hasattr(arg, '__iter__'):
1119
+ components = list(arg)
1120
+ else:
1121
+ components = [arg]
1122
+ else:
1123
+ components = list(args)
1124
+
1125
+ # Ensure even number of components
1126
+ if len(components) % 2 != 0:
1127
+ components.append(base_type(0))
1128
+
1129
+ half = len(components) // 2
1130
+ self.a = cebr(*components[:half])
1131
+ self.b = cebr(*components[half:])
1132
+
1133
+ @staticmethod
1134
+ def _ensure_cebr(value, cebr_class):
1135
+ """Convert value to cebr instance if needed."""
1136
+ if isinstance(value, cebr_class):
1137
+ return value
1138
+ # Handle complex numbers
1139
+ elif isinstance(value, complex):
1140
+ return cebr_class(value.real, value.imag)
1141
+ # Handle single values
1142
+ else:
1143
+ return cebr_class(value)
1144
+
1145
+ @classmethod
1146
+ def from_complex(cls, c: complex):
1147
+ """generate from a complex number."""
1148
+ return cls(c.real, c.imag)
1149
+
1150
+ @classmethod
1151
+ def from_pair(cls, a, b):
1152
+ """generate from a pair (a, b)."""
1153
+ return cls(a, b, pair=True)
1154
+
1155
+ @property
1156
+ def real(self) -> float:
1157
+ """Real part."""
1158
+ if hasattr(self.a, 'real'):
1159
+ return float(self.a.real)
1160
+ else:
1161
+ return float(self.a)
1162
+
1163
+ def coefficients(self):
1164
+ """Get all coefficients as a tuple."""
1165
+ if hasattr(self.a, 'coefficients'):
1166
+ a_coeffs = self.a.coefficients()
1167
+ else:
1168
+ a_coeffs = (float(self.a),)
1169
+
1170
+ if hasattr(self.b, 'coefficients'):
1171
+ b_coeffs = self.b.coefficients()
1172
+ else:
1173
+ b_coeffs = (float(self.b),)
1174
+
1175
+ return a_coeffs + b_coeffs
1176
+
1177
+ def __add__(self, other):
1178
+ if isinstance(other, CayleyDicksonCebr):
1179
+ return CayleyDicksonCebr(
1180
+ self.a + other.a,
1181
+ self.b + other.b,
1182
+ pair=True
1183
+ )
1184
+
1185
+ # Try to convert to this cebr
1186
+ try:
1187
+ other_cd = CayleyDicksonCebr(other)
1188
+ return self + other_cd
1189
+ except:
1190
+ return NotImplemented
1191
+
1192
+ def __radd__(self, other):
1193
+ return self.__add__(other)
1194
+
1195
+ def __sub__(self, other):
1196
+ if isinstance(other, CayleyDicksonCebr):
1197
+ return CayleyDicksonCebr(
1198
+ self.a - other.a,
1199
+ self.b - other.b,
1200
+ pair=True
1201
+ )
1202
+
1203
+ try:
1204
+ other_cd = CayleyDicksonCebr(other)
1205
+ return self - other_cd
1206
+ except:
1207
+ return NotImplemented
1208
+
1209
+ def __rsub__(self, other):
1210
+ try:
1211
+ other_cd = CayleyDicksonCebr(other)
1212
+ return other_cd - self
1213
+ except:
1214
+ return NotImplemented
1215
+
1216
+ def __mul__(self, other):
1217
+ if isinstance(other, CayleyDicksonCebr):
1218
+ # Cayley-Dickson multiplication
1219
+ a = self.a * other.a - other.b * self._conj_b()
1220
+ b = self._conj_a() * other.b + other.a * self.b
1221
+ return CayleyDicksonCebr(a, b, pair=True)
1222
+
1223
+ # Scalar multiplication
1224
+ try:
1225
+ other_cd = CayleyDicksonCebr(other)
1226
+ return self * other_cd
1227
+ except:
1228
+ return NotImplemented
1229
+
1230
+ def __rmul__(self, other):
1231
+ return self.__mul__(other)
1232
+
1233
+ def __truediv__(self, other):
1234
+ if isinstance(other, CayleyDicksonCebr):
1235
+ return self * other.inverse()
1236
+
1237
+ try:
1238
+ other_cd = CayleyDicksonCebr(other)
1239
+ return self / other_cd
1240
+ except:
1241
+ return NotImplemented
1242
+
1243
+ def __rtruediv__(self, other):
1244
+ try:
1245
+ other_cd = CayleyDicksonCebr(other)
1246
+ return other_cd / self
1247
+ except:
1248
+ return NotImplemented
1249
+
1250
+ def __neg__(self):
1251
+ return CayleyDicksonCebr(-self.a, -self.b, pair=True)
1252
+
1253
+ def __pos__(self):
1254
+ return self
1255
+
1256
+ def __abs__(self):
1257
+ return self.norm()
1258
+
1259
+ def __eq__(self, other):
1260
+ if isinstance(other, CayleyDicksonCebr):
1261
+ return self.a == other.a and self.b == other.b
1262
+
1263
+ try:
1264
+ other_cd = CayleyDicksonCebr(other)
1265
+ return self == other_cd
1266
+ except:
1267
+ return False
1268
+
1269
+ def __ne__(self, other):
1270
+ return not self.__eq__(other)
1271
+
1272
+ def __hash__(self):
1273
+ return hash((self.a, self.b))
1274
+
1275
+ def __str__(self):
1276
+ coeffs = self.coefficients()
1277
+ if len(coeffs) <= 8:
1278
+ return f"({', '.join(f'{c:.4f}' for c in coeffs)})"
1279
+ else:
1280
+ return f"CD[{len(coeffs)}]({coeffs[0]:.4f}, ..., {coeffs[-1]:.4f})"
1281
+
1282
+ def __repr__(self):
1283
+ return f"{self.__class__.__name__}({', '.join(map(str, self.coefficients()))})"
1284
+
1285
+ def _conj_a(self):
1286
+ """Conjugate of a."""
1287
+ if hasattr(self.a, 'conjugate'):
1288
+ return self.a.conjugate()
1289
+ return self.a
1290
+
1291
+ def _conj_b(self):
1292
+ """Conjugate of b."""
1293
+ if hasattr(self.b, 'conjugate'):
1294
+ return self.b.conjugate()
1295
+ return self.b
1296
+
1297
+ def conjugate(self):
1298
+ """Conjugate: conj(a, b) = (conj(a), -b)."""
1299
+ return CayleyDicksonCebr(
1300
+ self._conj_a(),
1301
+ -self.b,
1302
+ pair=True
1303
+ )
1304
+
1305
+ def norm(self) -> float:
1306
+ """Euclidean norm."""
1307
+
1308
+ def get_norm_squared(x):
1309
+ if hasattr(x, 'norm_squared'):
1310
+ return float(x.norm_squared())
1311
+ elif hasattr(x, 'norm'):
1312
+ n = float(x.norm())
1313
+ return n * n
1314
+ else:
1315
+ val = float(x)
1316
+ return val * val
1317
+
1318
+ norm_sq = get_norm_squared(self.a) + get_norm_squared(self.b)
1319
+ return math.sqrt(norm_sq)
1320
+
1321
+ def norm_squared(self):
1322
+ """Square of the norm."""
1323
+ def get_norm_squared(x):
1324
+ if hasattr(x, 'norm_squared'):
1325
+ return x.norm_squared()
1326
+ elif hasattr(x, 'norm'):
1327
+ n = x.norm()
1328
+ return n * n
1329
+ else:
1330
+ return x * x
1331
+
1332
+ return get_num_squared(self.a) + get_norm_squared(self.b)
1333
+
1334
+ def inverse(self):
1335
+ """Multiplicative inverse."""
1336
+ norm_sq = self.norm_squared()
1337
+ if float(norm_sq) == 0:
1338
+ raise ZeroDivisionError("Cannot invert zero element")
1339
+
1340
+ conj = self.conjugate()
1341
+ return CayleyDicksonCebr(
1342
+ conj.a / norm_sq,
1343
+ conj.b / norm_sq,
1344
+ pair=True
1345
+ )
1346
+
1347
+ # Set class attributes
1348
+ if hasattr(cebr, 'dimensions'):
1349
+ CayleyDicksonCebr.dimensions = cebr.dimensions * 2
1350
+ else:
1351
+ CayleyDicksonCebr.dimensions = 2
1352
+
1353
+ # DÜZELTME: 'algebra_class' yerine 'cebr' kullan
1354
+ CayleyDicksonCebr.__name__ = f"CD{cebr.__name__}"
1355
+
1356
+ return CayleyDicksonCebr
1357
+
1358
+
1359
+ def cayley_dickson_cebr(level: int, base_type=float) -> type:
1360
+ """generate Cayley-Dickson cebr of given level."""
1361
+ if not isinstance(level, int) or level < 0:
1362
+ raise ValueError(f"Level must be non-negative integer, got {level}")
1363
+
1364
+ # Start with real numbers
1365
+ if level == 0:
1366
+ class RealAlgebra:
1367
+ dimensions = 1
1368
+ base = base_type
1369
+
1370
+ def __init__(self, value):
1371
+ self.value = base_type(value)
1372
+
1373
+ def __add__(self, other):
1374
+ if isinstance(other, RealAlgebra):
1375
+ return RealAlgebra(self.value + other.value)
1376
+ return RealAlgebra(self.value + base_type(other))
1377
+
1378
+ def __mul__(self, other):
1379
+ if isinstance(other, RealAlgebra):
1380
+ return RealAlgebra(self.value * other.value)
1381
+ return RealAlgebra(self.value * base_type(other))
1382
+
1383
+ def __repr__(self):
1384
+ return f"RealAlgebra({self.value})"
1385
+
1386
+ @property
1387
+ def real(self):
1388
+ return float(self.value)
1389
+
1390
+ def conjugate(self):
1391
+ return self
1392
+
1393
+ def norm(self):
1394
+ return abs(float(self.value))
1395
+
1396
+ return RealAlgebra
1397
+
1398
+ # Apply construction level times
1399
+ current_cebr = cayley_dickson_cebr(0, base_type)
1400
+ for i in range(level):
1401
+ current_cebr = cayley_dickson_process(current_cebr, base_type)
1402
+
1403
+ # Set name
1404
+ if level == 0:
1405
+ current_cebr.__name__ = "Real"
1406
+ elif level == 1:
1407
+ current_cebr.__name__ = "Complex"
1408
+ elif level == 2:
1409
+ current_cebr.__name__ = "Quaternion"
1410
+ elif level == 3:
1411
+ current_cebr.__name__ = "Octonion"
1412
+ elif level == 4:
1413
+ current_cebr.__name__ = "Sedenion"
1414
+ elif level == 5:
1415
+ current_cebr.__name__ = "Pathion"
1416
+ elif level == 6:
1417
+ current_cebr.__name__ = "Chingon"
1418
+ elif level == 7:
1419
+ current_cebr.__name__ = "Routon"
1420
+ elif level == 8:
1421
+ current_cebr.__name__ = "Voudon"
1422
+ else:
1423
+ current_cebr.__name__ = f"CD{level}"
1424
+
1425
+ return current_cebr
1426
+
1427
+
1428
+ # =============================================
1429
+ # Utility Functions
1430
+ # =============================================
1431
+
1432
+ def generate_cd_chain(max_level: int = 8) -> list:
1433
+ """
1434
+ Generate chain of Cayley-Dickson algebras.
1435
+
1436
+ Args:
1437
+ max_level: Maximum level to generate
1438
+
1439
+ Returns:
1440
+ List of cebr classes
1441
+ """
1442
+ return [cayley_dickson_cebr(i) for i in range(max_level + 1)]
1443
+
1444
+
1445
+ def cd_number_from_components(level: int, *components) -> object:
1446
+ """
1447
+ generate a Cayley-Dickson number from components.
1448
+
1449
+ Args:
1450
+ level: cebr level
1451
+ *components: Number components
1452
+
1453
+ Returns:
1454
+ Cayley-Dickson number instance
1455
+ """
1456
+ cebr = cayley_dickson_cebr(level)
1457
+ return cebr(*components)
1458
+
1459
+ def _parse_complex(s: Any) -> ComplexNumber:
1460
+ """
1461
+ Parse input as complex number.
1462
+ Supports: "1,2", "1+2j", "3j", 5, 3.14, etc.
1463
+ """
1464
+ # If already ComplexNumber
1465
+ if isinstance(s, ComplexNumber):
1466
+ return s
1467
+
1468
+ # If complex
1469
+ if isinstance(s, complex):
1470
+ return ComplexNumber(s.real, s.imag)
1471
+
1472
+ # If numeric
1473
+ if isinstance(s, (int, float)):
1474
+ return ComplexNumber(float(s), 0.0)
1475
+
1476
+ # Convert to string
1477
+ if not isinstance(s, str):
1478
+ s = str(s)
1479
+
1480
+ s = s.strip().replace(' ', '').replace('J', 'j').replace('i', 'j')
1481
+
1482
+ # Try comma-separated
1483
+ if ',' in s:
1484
+ parts = s.split(',')
1485
+ if len(parts) == 2:
1486
+ try:
1487
+ return ComplexNumber(float(parts[0]), float(parts[1]))
1488
+ except ValueError:
1489
+ pass
1490
+
1491
+ # Try Python's complex parser
1492
+ try:
1493
+ c = complex(s)
1494
+ return ComplexNumber(c.real, c.imag)
1495
+ except ValueError:
1496
+ pass
1497
+
1498
+ # Try as real number
1499
+ try:
1500
+ return ComplexNumber(float(s), 0.0)
1501
+ except ValueError:
1502
+ pass
1503
+
1504
+ # Try as pure imaginary
1505
+ if s.endswith('j'):
1506
+ try:
1507
+ imag = float(s[:-1]) if s[:-1] not in ['', '+', '-'] else 1.0
1508
+ if s.startswith('-'):
1509
+ imag = -imag
1510
+ return ComplexNumber(0.0, imag)
1511
+ except ValueError:
1512
+ pass
1513
+
1514
+ # Fallback
1515
+ warnings.warn(f"Could not parse as complex: {repr(s)}", RuntimeWarning)
1516
+ return ComplexNumber(0.0, 0.0)
1517
+
1518
+
1519
+ def _parse_hypercomplex(s: Any, dimension: int) -> HypercomplexNumber:
1520
+ """Parse input as hypercomplex number of given dimension."""
1521
+ try:
1522
+ # If already HypercomplexNumber
1523
+ if isinstance(s, HypercomplexNumber):
1524
+ if s.dimension == dimension:
1525
+ return s
1526
+ elif s.dimension < dimension:
1527
+ return s.pad_to_dimension(dimension)
1528
+ else:
1529
+ return s.truncate_to_dimension(dimension)
1530
+
1531
+ # If numeric
1532
+ if isinstance(s, (int, float)):
1533
+ coeffs = [float(s)] + [0.0] * (dimension - 1)
1534
+ return HypercomplexNumber(*coeffs, dimension=dimension)
1535
+
1536
+ # If complex
1537
+ if isinstance(s, (complex, ComplexNumber)):
1538
+ if isinstance(s, complex):
1539
+ c = s
1540
+ else:
1541
+ c = s.to_complex()
1542
+ coeffs = [c.real, c.imag] + [0.0] * (dimension - 2)
1543
+ return HypercomplexNumber(*coeffs, dimension=dimension)
1544
+
1545
+ # If iterable (list, tuple, etc.)
1546
+ if hasattr(s, '__iter__') and not isinstance(s, str):
1547
+ coeffs = list(s)
1548
+ if len(coeffs) < dimension:
1549
+ coeffs = coeffs + [0.0] * (dimension - len(coeffs))
1550
+ elif len(coeffs) > dimension:
1551
+ coeffs = coeffs[:dimension]
1552
+ return HypercomplexNumber(*coeffs, dimension=dimension)
1553
+
1554
+ # String parsing
1555
+ if not isinstance(s, str):
1556
+ s = str(s)
1557
+
1558
+ s = s.strip()
1559
+ s = s.strip('[]{}()')
1560
+
1561
+ if not s:
1562
+ return HypercomplexNumber(*([0.0] * dimension), dimension=dimension)
1563
+
1564
+ # Comma-separated list
1565
+ if ',' in s:
1566
+ parts = [p.strip() for p in s.split(',') if p.strip()]
1567
+ if parts:
1568
+ try:
1569
+ coeffs = [float(p) for p in parts]
1570
+ if len(coeffs) < dimension:
1571
+ coeffs = coeffs + [0.0] * (dimension - len(coeffs))
1572
+ elif len(coeffs) > dimension:
1573
+ coeffs = coeffs[:dimension]
1574
+ return HypercomplexNumber(*coeffs, dimension=dimension)
1575
+ except ValueError:
1576
+ pass
1577
+
1578
+ # Single number
1579
+ try:
1580
+ coeffs = [float(s)] + [0.0] * (dimension - 1)
1581
+ return HypercomplexNumber(*coeffs, dimension=dimension)
1582
+ except ValueError:
1583
+ pass
1584
+
1585
+ # Complex string
1586
+ try:
1587
+ c = complex(s)
1588
+ coeffs = [c.real, c.imag] + [0.0] * (dimension - 2)
1589
+ return HypercomplexNumber(*coeffs, dimension=dimension)
1590
+ except ValueError:
1591
+ pass
1592
+
1593
+ except Exception as e:
1594
+ warnings.warn(f"Parse error for hypercomplex (dim={dimension}): {e}", RuntimeWarning)
1595
+
1596
+ # Fallback: zero
1597
+ return HypercomplexNumber(*([0.0] * dimension), dimension=dimension)
1598
+
1599
+
1600
+ def _parse_universal(s: Any, target_type: str) -> Any:
1601
+ """
1602
+ Universal parser for all number types.
1603
+
1604
+ Args:
1605
+ s: Input to parse
1606
+ target_type: "real", "complex", "quaternion", "octonion",
1607
+ "sedenion", "pathion", "chingon", "routon", "voudon",
1608
+ "bicomplex", "neutrosophic"
1609
+
1610
+ Returns:
1611
+ Parsed number
1612
+ """
1613
+ # Type mapping
1614
+ type_map = {
1615
+ "real": 1,
1616
+ "complex": 2,
1617
+ "quaternion": 4,
1618
+ "octonion": 8,
1619
+ "sedenion": 16,
1620
+ "pathion": 32,
1621
+ "chingon": 64,
1622
+ "routon": 128,
1623
+ "voudon": 256
1624
+ }
1625
+
1626
+ try:
1627
+ # Special cases
1628
+ if target_type == "bicomplex":
1629
+ # Parse bicomplex (4 components: re1, im1, re2, im2)
1630
+ if isinstance(s, BicomplexNumber):
1631
+ return s
1632
+
1633
+ if isinstance(s, str):
1634
+ s = s.strip().strip('[]{}()')
1635
+
1636
+ # Try to parse as 4 numbers
1637
+ if hasattr(s, '__iter__') and not isinstance(s, str):
1638
+ coeffs = list(s)
1639
+ elif isinstance(s, str) and ',' in s:
1640
+ coeffs = [float(p.strip()) for p in s.split(',') if p.strip()]
1641
+ else:
1642
+ coeffs = [float(s), 0.0, 0.0, 0.0]
1643
+
1644
+ if len(coeffs) < 4:
1645
+ coeffs = coeffs + [0.0] * (4 - len(coeffs))
1646
+
1647
+ z1 = ComplexNumber(coeffs[0], coeffs[1])
1648
+ z2 = ComplexNumber(coeffs[2], coeffs[3])
1649
+ return BicomplexNumber(z1, z2)
1650
+
1651
+ elif target_type == "neutrosophic":
1652
+ # Parse neutrosophic (2 components: a, b)
1653
+ if isinstance(s, NeutrosophicNumber):
1654
+ return s
1655
+
1656
+ if isinstance(s, str):
1657
+ s = s.strip().strip('[]{}()')
1658
+
1659
+ if hasattr(s, '__iter__') and not isinstance(s, str):
1660
+ coeffs = list(s)
1661
+ elif isinstance(s, str) and ',' in s:
1662
+ coeffs = [float(p.strip()) for p in s.split(',') if p.strip()]
1663
+ else:
1664
+ coeffs = [float(s), 0.0]
1665
+
1666
+ if len(coeffs) < 2:
1667
+ coeffs = coeffs + [0.0] * (2 - len(coeffs))
1668
+
1669
+ return NeutrosophicNumber(coeffs[0], coeffs[1])
1670
+
1671
+ # Standard hypercomplex types
1672
+ elif target_type in type_map:
1673
+ dimension = type_map[target_type]
1674
+ return _parse_hypercomplex(s, dimension)
1675
+
1676
+ else:
1677
+ raise ValueError(f"Unknown target type: {target_type}")
1678
+
1679
+ except Exception as e:
1680
+ warnings.warn(f"Parse error for {target_type}: {e}", RuntimeWarning)
1681
+
1682
+ # Return default value
1683
+ defaults = {
1684
+ "real": 0.0,
1685
+ "complex": ComplexNumber(0, 0),
1686
+ "quaternion": HypercomplexNumber(0, 0, 0, 0, dimension=4),
1687
+ "octonion": HypercomplexNumber(*([0.0] * 8), dimension=8),
1688
+ "sedenion": HypercomplexNumber(*([0.0] * 16), dimension=16),
1689
+ "pathion": HypercomplexNumber(*([0.0] * 32), dimension=32),
1690
+ "chingon": HypercomplexNumber(*([0.0] * 64), dimension=64),
1691
+ "routon": HypercomplexNumber(*([0.0] * 128), dimension=128),
1692
+ "voudon": HypercomplexNumber(*([0.0] * 256), dimension=256),
1693
+ "bicomplex": BicomplexNumber(ComplexNumber(0, 0), ComplexNumber(0, 0)),
1694
+ "neutrosophic": NeutrosophicNumber(0.0, 0.0)
1695
+ }
1696
+
1697
+ return defaults.get(target_type, None)
1698
+
1699
+ # =============================================
1700
+ # Mathematical Sequences and Functions
1701
+ # =============================================
1702
+
1703
+ def oresme_sequence(n_terms: int) -> List[float]:
1704
+ """Generate Oresme sequence: n / 2^n."""
1705
+ return [n / (2 ** n) for n in range(1, n_terms + 1)]
1706
+
1707
+
1708
+ def harmonic_numbers(n_terms: int) -> Generator[Fraction, None, None]:
1709
+ """Generate harmonic numbers: H_n = 1 + 1/2 + ... + 1/n."""
1710
+ current = Fraction(0)
1711
+ for n in range(1, n_terms + 1):
1712
+ current += Fraction(1, n)
1713
+ yield current
1714
+
1715
+
1716
+ def binet_formula(n: int) -> float:
1717
+ """Calculate nth Fibonacci number using Binet's formula."""
1718
+ sqrt5 = math.sqrt(5)
1719
+ phi = (1 + sqrt5) / 2
1720
+ psi = (1 - sqrt5) / 2
1721
+ return (phi**n - psi**n) / sqrt5
1722
+
1723
+
1724
+ def generate_cd_chain_names(max_level: int = 8) -> List[str]:
1725
+ """Generate names of Cayley-Dickson algebras up to given level."""
1726
+ names = ["Real", "Complex", "Quaternion", "Octonion", "Sedenion",
1727
+ "Pathion", "Chingon", "Routon", "Voudon"]
1728
+ return names[:max_level + 1]
1729
+ # =============================================
1730
+ # Example Usage and Tests
1731
+ # =============================================
1732
+
1733
+ if __name__ == "__main__":
1734
+ print("Advanced Number Systems Library")
1735
+ print("=" * 60)
1736
+
1737
+ # Test HypercomplexNumber (Real)
1738
+ print("\n1. Testing HypercomplexNumber (Real):")
1739
+ r1 = Real(3.14)
1740
+ r2 = Real(2.71)
1741
+ print(f" Real(3.14) = {r1}")
1742
+ print(f" Real(2.71) = {r2}")
1743
+ print(f" r1 + r2 = {r1 + r2}")
1744
+ print(f" r1 * r2 = {r1 * r2}")
1745
+
1746
+ # Test HypercomplexNumber (Complex)
1747
+ print("\n2. Testing HypercomplexNumber (Complex):")
1748
+ c1 = Complex(3, 4)
1749
+ c2 = Complex(1, 2)
1750
+ print(f" Complex(3, 4) = {c1}")
1751
+ print(f" Complex(1, 2) = {c2}")
1752
+ print(f" c1 + c2 = {c1 + c2}")
1753
+ print(f" c1 * c2 = {c1 * c2}")
1754
+ print(f" |c1| = {c1.norm():.2f}")
1755
+
1756
+ # Test HypercomplexNumber (Quaternion)
1757
+ print("\n3. Testing HypercomplexNumber (Quaternion):")
1758
+ q1 = Quaternion(1, 2, 3, 4)
1759
+ q2 = Quaternion(5, 6, 7, 8)
1760
+ print(f" Quaternion(1,2,3,4) = {q1}")
1761
+ print(f" Quaternion(5,6,7,8) = {q2}")
1762
+ print(f" q1 + q2 = {q1 + q2}")
1763
+ print(f" q1 * q2 = {q1 * q2}")
1764
+ print(f" Note: Quaternion multiplication is non-commutative")
1765
+ print(f" q2 * q1 = {q2 * q1}")
1766
+ print(f" q1 * q2 == q2 * q1? {q1 * q2 == q2 * q1}")
1767
+
1768
+ # Test BicomplexNumber
1769
+ print("\n4. Testing BicomplexNumber:")
1770
+ bc1 = Bicomplex(1, 2, 3, 4)
1771
+ bc2 = Bicomplex(5, 6, 7, 8)
1772
+ print(f" Bicomplex(1,2,3,4) = {bc1}")
1773
+ print(f" Bicomplex(5,6,7,8) = {bc2}")
1774
+ print(f" bc1 + bc2 = {bc1 + bc2}")
1775
+ print(f" bc1 * bc2 = {bc1 * bc2}")
1776
+
1777
+ # Test NeutrosophicNumber
1778
+ print("\n5. Testing NeutrosophicNumber:")
1779
+ n1 = Neutrosophic(3, 2)
1780
+ n2 = Neutrosophic(1, 4)
1781
+ print(f" Neutrosophic(3, 2) = {n1}")
1782
+ print(f" n1 + n2 = {n1 + n2}")
1783
+ print(f" n1 * n2 = {n1 * n2}")
1784
+
1785
+ # Test mixed operations
1786
+ print("\n6. Testing mixed operations:")
1787
+ real_num = Real(5.0)
1788
+ complex_num = Complex(3, 4)
1789
+ print(f" Real(5) * Complex(3,4) = {real_num * complex_num}")
1790
+ print(f" Complex(3,4) * Real(5) = {complex_num * real_num}")
1791
+
1792
+ # Test conversion
1793
+ print("\n7. Testing conversions:")
1794
+ print(f" Real(5).to_complex() = {Real(5).to_complex()}")
1795
+ print(f" Complex(3,4).to_complex() = {Complex(3,4).to_complex()}")
1796
+
1797
+ # Test with Python built-in types
1798
+ print("\n8. Testing with Python built-in types:")
1799
+ print(f" Complex(3,4) + 5 = {Complex(3,4) + 5}")
1800
+ print(f" 5 + Complex(3,4) = {5 + Complex(3,4)}")
1801
+ print(f" Complex(3,4) * 2 = {Complex(3,4) * 2}")
1802
+ print(f" 2 * Complex(3,4) = {2 * Complex(3,4)}")
1803
+
1804
+ print("\n✓ All tests completed successfully!")