kececinumbers 0.0.0__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.
@@ -0,0 +1,913 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Keçeci Numbers Module (kececinumbers.py)
4
+
5
+ This module provides a comprehensive framework for generating, analyzing, and
6
+ visualizing Keçeci Numbers across various number systems. It supports 11
7
+ distinct types, from standard integers and complex numbers to more exotic
8
+ constructs like neutrosophic and bicomplex numbers.
9
+
10
+ The core of the module is the `unified_generator`, which implements the
11
+ specific algorithm for creating Keçeci Number sequences. High-level functions
12
+ are available for easy interaction, parameter-based generation, and plotting.
13
+
14
+ Key Features:
15
+ - Generation of 11 types of Keçeci Numbers.
16
+ - A robust, unified algorithm for all number types.
17
+ - Helper functions for mathematical properties like primality and divisibility.
18
+ - Advanced plotting capabilities tailored to each number system.
19
+ - Functions for interactive use or programmatic integration.
20
+ ---
21
+
22
+ Keçeci Conjecture: Keçeci Varsayımı, Keçeci-Vermutung, Conjecture de Keçeci, Гипотеза Кечеджи, 凯杰西猜想, ケジェジ予想, Keçeci Huds, Keçeci Hudsiye, Keçeci Hudsia, كَچَه جِي ,حدس کچه جی, کچہ جی حدسیہ
23
+
24
+ Keçeci Varsayımı (Keçeci Conjecture) - Önerilen
25
+
26
+ Her Keçeci Sayı türü için, `unified_generator` fonksiyonu tarafından oluşturulan dizilerin, sonlu adımdan sonra periyodik bir yapıya veya tekrar eden bir asal temsiline (Keçeci Asal Sayısı, KPN) yakınsadığı sanılmaktadır. Bu davranış, Collatz Varsayımı'nın çoklu cebirsel sistemlere genişletilmiş bir hali olarak değerlendirilebilir.
27
+
28
+ Henüz kanıtlanmamıştır ve bu modül bu varsayımı test etmek için bir çerçeve sunar.
29
+ """
30
+
31
+ # --- Standard Library Imports ---
32
+ import collections
33
+ from dataclasses import dataclass
34
+ from fractions import Fraction
35
+ import math
36
+ from matplotlib.gridspec import GridSpec
37
+ import matplotlib.pyplot as plt
38
+ import numpy as np
39
+ import quaternion
40
+ import random
41
+ import re
42
+ import sympy
43
+ from typing import Any, Dict, List, Optional, Tuple
44
+
45
+
46
+ # ==============================================================================
47
+ # --- MODULE CONSTANTS: KEÇECI NUMBER TYPES ---
48
+ # ==============================================================================
49
+ TYPE_POSITIVE_REAL = 1
50
+ TYPE_NEGATIVE_REAL = 2
51
+ TYPE_COMPLEX = 3
52
+ TYPE_FLOAT = 4
53
+ TYPE_RATIONAL = 5
54
+ TYPE_QUATERNION = 6
55
+ TYPE_NEUTROSOPHIC = 7
56
+ TYPE_NEUTROSOPHIC_COMPLEX = 8
57
+ TYPE_HYPERREAL = 9
58
+ TYPE_BICOMPLEX = 10
59
+ TYPE_NEUTROSOPHIC_BICOMPLEX = 11
60
+
61
+ # ==============================================================================
62
+ # --- CUSTOM NUMBER CLASS DEFINITIONS ---
63
+ # ==============================================================================
64
+
65
+ @dataclass
66
+ class NeutrosophicNumber:
67
+ """Represents a neutrosophic number of the form a + bI where I^2 = I."""
68
+ a: float
69
+ b: float
70
+
71
+ def __add__(self, other: Any) -> "NeutrosophicNumber":
72
+ if isinstance(other, NeutrosophicNumber):
73
+ return NeutrosophicNumber(self.a + other.a, self.b + other.b)
74
+ if isinstance(other, (int, float)):
75
+ return NeutrosophicNumber(self.a + other, self.b)
76
+ return NotImplemented
77
+
78
+ def __sub__(self, other: Any) -> "NeutrosophicNumber":
79
+ if isinstance(other, NeutrosophicNumber):
80
+ return NeutrosophicNumber(self.a - other.a, self.b - other.b)
81
+ if isinstance(other, (int, float)):
82
+ return NeutrosophicNumber(self.a - other, self.b)
83
+ return NotImplemented
84
+
85
+ def __mul__(self, other: Any) -> "NeutrosophicNumber":
86
+ if isinstance(other, NeutrosophicNumber):
87
+ return NeutrosophicNumber(
88
+ self.a * other.a,
89
+ self.a * other.b + self.b * other.a + self.b * other.b,
90
+ )
91
+ if isinstance(other, (int, float)):
92
+ return NeutrosophicNumber(self.a * other, self.b * other)
93
+ return NotImplemented
94
+
95
+ def __truediv__(self, divisor: float) -> "NeutrosophicNumber":
96
+ if isinstance(divisor, (int, float)):
97
+ if divisor == 0:
98
+ raise ZeroDivisionError("Cannot divide by zero.")
99
+ return NeutrosophicNumber(self.a / divisor, self.b / divisor)
100
+ raise TypeError("Only scalar division is supported.")
101
+
102
+ def __str__(self) -> str:
103
+ return f"{self.a} + {self.b}I"
104
+
105
+ @dataclass
106
+ class NeutrosophicComplexNumber:
107
+ """Represents a number with a complex part and an indeterminacy level."""
108
+ real: float = 0.0
109
+ imag: float = 0.0
110
+ indeterminacy: float = 0.0
111
+
112
+ def __repr__(self) -> str:
113
+ return f"NeutrosophicComplexNumber(real={self.real}, imag={self.imag}, indeterminacy={self.indeterminacy})"
114
+
115
+ def __str__(self) -> str:
116
+ return f"({self.real}{self.imag:+}j) + {self.indeterminacy}I"
117
+
118
+ def __add__(self, other: Any) -> "NeutrosophicComplexNumber":
119
+ if isinstance(other, NeutrosophicComplexNumber):
120
+ return NeutrosophicComplexNumber(
121
+ self.real + other.real, self.imag + other.imag, self.indeterminacy + other.indeterminacy
122
+ )
123
+ if isinstance(other, (int, float)):
124
+ return NeutrosophicComplexNumber(self.real + other, self.imag, self.indeterminacy)
125
+ return NotImplemented
126
+
127
+ def __sub__(self, other: Any) -> "NeutrosophicComplexNumber":
128
+ if isinstance(other, NeutrosophicComplexNumber):
129
+ return NeutrosophicComplexNumber(
130
+ self.real - other.real, self.imag - other.imag, self.indeterminacy - other.indeterminacy
131
+ )
132
+ if isinstance(other, (int, float)):
133
+ return NeutrosophicComplexNumber(self.real - other, self.imag, self.indeterminacy)
134
+ return NotImplemented
135
+
136
+ def __mul__(self, other: Any) -> "NeutrosophicComplexNumber":
137
+ if isinstance(other, NeutrosophicComplexNumber):
138
+ new_real = self.real * other.real - self.imag * other.imag
139
+ new_imag = self.real * other.imag + self.imag * other.real
140
+ new_indeterminacy = self.indeterminacy + other.indeterminacy + (self.magnitude_sq() * other.indeterminacy)
141
+ return NeutrosophicComplexNumber(new_real, new_imag, new_indeterminacy)
142
+ if isinstance(other, complex):
143
+ new_real = self.real * other.real - self.imag * other.imag
144
+ new_imag = self.real * other.imag + self.imag * other.real
145
+ return NeutrosophicComplexNumber(new_real, new_imag, self.indeterminacy)
146
+ if isinstance(other, (int, float)):
147
+ return NeutrosophicComplexNumber(self.real * other, self.imag * other, self.indeterminacy * other)
148
+ return NotImplemented
149
+
150
+ def __truediv__(self, divisor: float) -> "NeutrosophicComplexNumber":
151
+ if isinstance(divisor, (int, float)):
152
+ if divisor == 0:
153
+ raise ZeroDivisionError("Cannot divide by zero.")
154
+ return NeutrosophicComplexNumber(
155
+ self.real / divisor, self.imag / divisor, self.indeterminacy / divisor
156
+ )
157
+ raise TypeError("Only scalar division is supported.")
158
+
159
+ def __radd__(self, other: Any) -> "NeutrosophicComplexNumber":
160
+ return self.__add__(other)
161
+
162
+ def __rsub__(self, other: Any) -> "NeutrosophicComplexNumber":
163
+ if isinstance(other, (int, float)):
164
+ return NeutrosophicComplexNumber(other - self.real, -self.imag, -self.indeterminacy)
165
+ return NotImplemented
166
+
167
+ def __rmul__(self, other: Any) -> "NeutrosophicComplexNumber":
168
+ return self.__mul__(other)
169
+
170
+ def magnitude_sq(self) -> float:
171
+ return self.real**2 + self.imag**2
172
+
173
+ @dataclass
174
+ class HyperrealNumber:
175
+ """Represents a hyperreal number as a sequence of real numbers."""
176
+ sequence: list
177
+
178
+ def __add__(self, other: Any) -> "HyperrealNumber":
179
+ if isinstance(other, HyperrealNumber):
180
+ return HyperrealNumber([a + b for a, b in zip(self.sequence, other.sequence)])
181
+ return NotImplemented
182
+
183
+ def __sub__(self, other: Any) -> "HyperrealNumber":
184
+ if isinstance(other, HyperrealNumber):
185
+ return HyperrealNumber([a - b for a, b in zip(self.sequence, other.sequence)])
186
+ return NotImplemented
187
+
188
+ def __mul__(self, scalar: float) -> "HyperrealNumber":
189
+ if isinstance(scalar, (int, float)):
190
+ return HyperrealNumber([x * scalar for x in self.sequence])
191
+ return NotImplemented
192
+
193
+ def __rmul__(self, scalar: float) -> "HyperrealNumber":
194
+ return self.__mul__(scalar)
195
+
196
+ def __truediv__(self, divisor: float) -> "HyperrealNumber":
197
+ if isinstance(divisor, (int, float)):
198
+ if divisor == 0:
199
+ raise ZeroDivisionError("Scalar division by zero.")
200
+ return HyperrealNumber([x / divisor for x in self.sequence])
201
+ raise TypeError("Only scalar division is supported.")
202
+
203
+ def __mod__(self, divisor: float) -> List[float]:
204
+ if isinstance(divisor, (int, float)):
205
+ return [x % divisor for x in self.sequence]
206
+ raise TypeError("Modulo only supported with a scalar divisor.")
207
+
208
+ def __str__(self) -> str:
209
+ return f"Hyperreal({self.sequence[:30]}...)"
210
+
211
+ @dataclass
212
+ class BicomplexNumber:
213
+ """Represents a bicomplex number z1 + j*z2, where i^2 = j^2 = -1."""
214
+ z1: complex
215
+ z2: complex
216
+
217
+ def __add__(self, other: Any) -> "BicomplexNumber":
218
+ if isinstance(other, BicomplexNumber):
219
+ return BicomplexNumber(self.z1 + other.z1, self.z2 + other.z2)
220
+ return NotImplemented
221
+
222
+ def __sub__(self, other: Any) -> "BicomplexNumber":
223
+ if isinstance(other, BicomplexNumber):
224
+ return BicomplexNumber(self.z1 - other.z1, self.z2 - other.z2)
225
+ return NotImplemented
226
+
227
+ def __mul__(self, other: Any) -> "BicomplexNumber":
228
+ if isinstance(other, BicomplexNumber):
229
+ return BicomplexNumber(
230
+ (self.z1 * other.z1) - (self.z2 * other.z2),
231
+ (self.z1 * other.z2) + (self.z2 * other.z1),
232
+ )
233
+ return NotImplemented
234
+
235
+ def __truediv__(self, scalar: float) -> "BicomplexNumber":
236
+ if isinstance(scalar, (int, float)):
237
+ if scalar == 0:
238
+ raise ZeroDivisionError("Cannot divide by zero.")
239
+ return BicomplexNumber(self.z1 / scalar, self.z2 / scalar)
240
+ raise TypeError("Only scalar division is supported.")
241
+
242
+ def __str__(self) -> str:
243
+ return f"Bicomplex({self.z1}, {self.z2})"
244
+
245
+ @dataclass
246
+ class NeutrosophicBicomplexNumber:
247
+ """Represents a simplified neutrosophic-bicomplex number."""
248
+ real: float
249
+ imag: float
250
+ neut_real: float
251
+ neut_imag: float
252
+ j_real: float
253
+ j_imag: float
254
+ j_neut_real: float
255
+ j_neut_imag: float
256
+
257
+ def __add__(self, other: Any) -> "NeutrosophicBicomplexNumber":
258
+ if isinstance(other, NeutrosophicBicomplexNumber):
259
+ return NeutrosophicBicomplexNumber(*(a + b for a, b in zip(self.__dict__.values(), other.__dict__.values())))
260
+ return NotImplemented
261
+
262
+ def __sub__(self, other: Any) -> "NeutrosophicBicomplexNumber":
263
+ if isinstance(other, NeutrosophicBicomplexNumber):
264
+ return NeutrosophicBicomplexNumber(*(a - b for a, b in zip(self.__dict__.values(), other.__dict__.values())))
265
+ return NotImplemented
266
+
267
+ def __truediv__(self, scalar: float) -> "NeutrosophicBicomplexNumber":
268
+ if isinstance(scalar, (int, float)):
269
+ if scalar == 0:
270
+ raise ZeroDivisionError("Cannot divide by zero.")
271
+ return NeutrosophicBicomplexNumber(*(val / scalar for val in self.__dict__.values()))
272
+ raise TypeError("Only scalar division supported.")
273
+
274
+ def __str__(self) -> str:
275
+ return f"NeutroBicomplex(r={self.real}, i={self.imag}, Ir={self.neut_real}, ...)"
276
+
277
+ # ==============================================================================
278
+ # --- HELPER FUNCTIONS ---
279
+ # ==============================================================================
280
+
281
+ def _get_integer_representation(n_input: Any) -> Optional[int]:
282
+ """Extracts the primary integer component from any supported number type."""
283
+ try:
284
+ if isinstance(n_input, (int, float, Fraction)):
285
+ return abs(int(n_input))
286
+ if isinstance(n_input, complex):
287
+ return abs(int(n_input.real))
288
+ if isinstance(n_input, np.quaternion):
289
+ return abs(int(n_input.w))
290
+ if isinstance(n_input, NeutrosophicNumber):
291
+ return abs(int(n_input.a))
292
+ if isinstance(n_input, NeutrosophicComplexNumber):
293
+ return abs(int(n_input.real))
294
+ if isinstance(n_input, HyperrealNumber):
295
+ return abs(int(n_input.sequence[0])) if n_input.sequence else 0
296
+ if isinstance(n_input, BicomplexNumber):
297
+ return abs(int(n_input.z1.real))
298
+ if isinstance(n_input, NeutrosophicBicomplexNumber):
299
+ return abs(int(n_input.real))
300
+ return abs(int(n_input))
301
+ except (ValueError, TypeError, IndexError):
302
+ return None
303
+
304
+ def is_prime(n_input: Any) -> bool:
305
+ """
306
+ Checks if a given number (or its principal component) is prime
307
+ using the robust sympy.isprime function.
308
+ """
309
+ # Adım 1: Karmaşık sayı türünden tamsayıyı çıkarma (Bu kısım aynı kalıyor)
310
+ value_to_check = _get_integer_representation(n_input)
311
+
312
+ # Adım 2: Tamsayı geçerli değilse False döndür
313
+ if value_to_check is None:
314
+ return False
315
+
316
+ # Adım 3: Asallık testini sympy'ye bırak
317
+ # sympy.isprime, 2'den küçük sayılar (1, 0, negatifler) için zaten False döndürür.
318
+ return sympy.isprime(value_to_check)
319
+
320
+ """
321
+ def is_prime(n_input: Any) -> bool:
322
+ #Checks if a given number (or its principal component) is prime.
323
+ value_to_check = _get_integer_representation(n_input)
324
+ if value_to_check is None or value_to_check < 2:
325
+ return False
326
+ if value_to_check == 2:
327
+ return True
328
+ if value_to_check % 2 == 0:
329
+ return False
330
+ for i in range(3, int(math.sqrt(value_to_check)) + 1, 2):
331
+ if value_to_check % i == 0:
332
+ return False
333
+ return True
334
+ """
335
+
336
+ def _is_divisible(value: Any, divisor: int, kececi_type: int) -> bool:
337
+ """Helper to check divisibility for different number types."""
338
+ try:
339
+ if kececi_type in [TYPE_POSITIVE_REAL, TYPE_NEGATIVE_REAL]:
340
+ return value % divisor == 0
341
+ if kececi_type == TYPE_FLOAT:
342
+ return math.isclose(value % divisor, 0)
343
+ if kececi_type == TYPE_RATIONAL:
344
+ return (value / divisor).denominator == 1
345
+ if kececi_type == TYPE_COMPLEX:
346
+ return math.isclose(value.real % divisor, 0) and math.isclose(value.imag % divisor, 0)
347
+ if kececi_type == TYPE_QUATERNION:
348
+ return all(math.isclose(c % divisor, 0) for c in [value.w, value.x, value.y, value.z])
349
+ if kececi_type == TYPE_NEUTROSOPHIC:
350
+ return math.isclose(value.a % divisor, 0) and math.isclose(value.b % divisor, 0)
351
+ if kececi_type == TYPE_NEUTROSOPHIC_COMPLEX:
352
+ return all(math.isclose(c % divisor, 0) for c in [value.real, value.imag, value.indeterminacy])
353
+ if kececi_type == TYPE_HYPERREAL:
354
+ return all(math.isclose(x % divisor, 0) for x in value.sequence)
355
+ if kececi_type == TYPE_BICOMPLEX:
356
+ return _is_divisible(value.z1, divisor, TYPE_COMPLEX) and _is_divisible(value.z2, divisor, TYPE_COMPLEX)
357
+ if kececi_type == TYPE_NEUTROSOPHIC_BICOMPLEX:
358
+ return all(math.isclose(c % divisor, 0) for c in value.__dict__.values())
359
+ except (TypeError, ValueError):
360
+ return False
361
+ return False
362
+
363
+ def _parse_complex(s: str) -> complex:
364
+ """Parses a string into a complex number. '3' becomes 3+3j."""
365
+ s_clean = s.strip().lower()
366
+ try:
367
+ c = complex(s_clean)
368
+ if c.imag == 0 and 'j' not in s_clean:
369
+ return complex(c.real, c.real)
370
+ return c
371
+ except ValueError as e:
372
+ raise ValueError(f"Invalid complex number format: '{s}'") from e
373
+
374
+ def _parse_neutrosophic(s: str) -> Tuple[float, float]:
375
+ """Parses a neutrosophic string 'a+bI' into a tuple (a, b)."""
376
+ s = s.strip().replace(" ", "").upper()
377
+ if not s:
378
+ return 0.0, 0.0
379
+
380
+ if 'I' not in s:
381
+ return float(s), 0.0
382
+
383
+ pattern = re.compile(r"^(?P<a>[+-]?\d+\.?\d*)?(?P<b>[+-]?)I$")
384
+ match = re.match(r"^(?P<val>[+-]?\d+\.?\d*)$", s)
385
+ if match: # Just a number
386
+ return float(match.group('val')), 0.0
387
+
388
+ pattern = re.compile(r"^(?P<a>[+-]?\d+\.?\d*)?(?P<b>[+-]?\d*\.?\d*)I$")
389
+ full_match = pattern.match(s)
390
+ if not full_match:
391
+ raise ValueError(f"Invalid neutrosophic format: {s}")
392
+
393
+ parts = full_match.groupdict()
394
+ a_part = parts.get('a') or "0"
395
+ b_part = parts.get('b')
396
+
397
+ if b_part in (None, "", "+"):
398
+ b_val = 1.0
399
+ elif b_part == "-":
400
+ b_val = -1.0
401
+ else:
402
+ b_val = float(b_part)
403
+
404
+ return float(a_part), b_val
405
+
406
+
407
+ def _parse_hyperreal(s: str) -> Tuple[float, float]:
408
+ """Parses 'a+be' string into a tuple (a, b)."""
409
+ s = s.strip().replace(" ", "").lower()
410
+ if not s:
411
+ return 0.0, 0.0
412
+ if 'e' not in s:
413
+ return float(s), 0.0
414
+
415
+ pattern = re.compile(r"^(?P<a>[+-]?\d+\.?\d*)?(?P<b>[+-]?\d*\.?\d*)e$")
416
+ match = pattern.match(s)
417
+ if not match:
418
+ raise ValueError(f"Invalid hyperreal format: {s}")
419
+
420
+ parts = match.groupdict()
421
+ a_part = parts.get('a') or "0"
422
+ b_part = parts.get('b')
423
+
424
+ if b_part in (None, "", "+"):
425
+ b_val = 1.0
426
+ elif b_part == "-":
427
+ b_val = -1.0
428
+ else:
429
+ b_val = float(b_part)
430
+
431
+ return float(a_part), b_val
432
+
433
+ def _parse_quaternion(s: str) -> np.quaternion:
434
+ """Parses user string ('a+bi+cj+dk' or scalar) into a quaternion."""
435
+ s_clean = s.replace(" ", "").lower()
436
+ if not s_clean:
437
+ raise ValueError("Input cannot be empty.")
438
+
439
+ try:
440
+ val = float(s_clean)
441
+ return np.quaternion(val, val, val, val)
442
+ except ValueError:
443
+ pass
444
+
445
+ s_temp = re.sub(r'([+-])([ijk])', r'\g<1>1\g<2>', s_clean)
446
+ if s_temp.startswith(('i', 'j', 'k')):
447
+ s_temp = '1' + s_temp
448
+
449
+ pattern = re.compile(r'([+-]?\d*\.?\d*)([ijk])?')
450
+ matches = pattern.findall(s_temp)
451
+
452
+ parts = {'w': 0.0, 'x': 0.0, 'y': 0.0, 'z': 0.0}
453
+ for value_str, component in matches:
454
+ if not value_str:
455
+ continue
456
+ value = float(value_str)
457
+ if component == 'i':
458
+ parts['x'] += value
459
+ elif component == 'j':
460
+ parts['y'] += value
461
+ elif component == 'k':
462
+ parts['z'] += value
463
+ else:
464
+ parts['w'] += value
465
+
466
+ return np.quaternion(parts['w'], parts['x'], parts['y'], parts['z'])
467
+
468
+ def get_random_type(num_iterations: int, fixed_start_raw: str = "0", fixed_add_base_scalar: float = 9.0) -> List[Any]:
469
+ """Generates Keçeci Numbers for a randomly selected type."""
470
+ random_type_choice = random.randint(1, 11)
471
+ type_names_list = [
472
+ "Positive Real", "Negative Real", "Complex", "Float", "Rational",
473
+ "Quaternion", "Neutrosophic", "Neutro-Complex", "Hyperreal",
474
+ "Bicomplex", "Neutro-Bicomplex"
475
+ ]
476
+ print(f"\nRandomly selected Keçeci Number Type: {random_type_choice} ({type_names_list[random_type_choice-1]})")
477
+
478
+ return get_with_params(
479
+ kececi_type_choice=random_type_choice,
480
+ iterations=num_iterations,
481
+ start_value_raw=fixed_start_raw,
482
+ add_value_base_scalar=fixed_add_base_scalar
483
+ )
484
+
485
+ # ==============================================================================
486
+ # --- CORE GENERATOR ---
487
+ # ==============================================================================
488
+
489
+ def unified_generator(kececi_type: int, start_input_raw: str, add_input_base_scalar: float, iterations: int) -> List[Any]:
490
+ """Core engine to generate Keçeci Number sequences."""
491
+ current_value = None
492
+ add_value_typed = None
493
+ ask_unit = None
494
+ use_integer_division = False
495
+
496
+ try:
497
+ a_float = float(add_input_base_scalar)
498
+
499
+ if kececi_type in [TYPE_POSITIVE_REAL, TYPE_NEGATIVE_REAL]:
500
+ current_value = int(float(start_input_raw))
501
+ add_value_typed = int(a_float)
502
+ ask_unit = 1
503
+ use_integer_division = True
504
+ elif kececi_type == TYPE_FLOAT:
505
+ current_value = float(start_input_raw)
506
+ add_value_typed = a_float
507
+ ask_unit = 1.0
508
+ elif kececi_type == TYPE_RATIONAL:
509
+ current_value = Fraction(start_input_raw)
510
+ add_value_typed = Fraction(add_input_base_scalar)
511
+ ask_unit = Fraction(1)
512
+ elif kececi_type == TYPE_COMPLEX:
513
+ current_value = _parse_complex(start_input_raw)
514
+ add_value_typed = complex(a_float, a_float)
515
+ ask_unit = 1 + 1j
516
+ elif kececi_type == TYPE_NEUTROSOPHIC:
517
+ a, b = _parse_neutrosophic(start_input_raw)
518
+ current_value = NeutrosophicNumber(a, b)
519
+ add_value_typed = NeutrosophicNumber(a_float, 0)
520
+ ask_unit = NeutrosophicNumber(1, 1)
521
+ elif kececi_type == TYPE_NEUTROSOPHIC_COMPLEX:
522
+ s_complex = _parse_complex(start_input_raw)
523
+ current_value = NeutrosophicComplexNumber(s_complex.real, s_complex.imag, 0.0)
524
+ add_value_typed = NeutrosophicComplexNumber(a_float, 0.0, 0.0)
525
+ ask_unit = NeutrosophicComplexNumber(1, 1, 1)
526
+ elif kececi_type == TYPE_HYPERREAL:
527
+ a, b = _parse_hyperreal(start_input_raw)
528
+ sequence_list = [a + b / n for n in range(1, 11)]
529
+ current_value = HyperrealNumber(sequence_list)
530
+ add_sequence = [a_float] + [0.0] * 9
531
+ add_value_typed = HyperrealNumber(add_sequence)
532
+ ask_unit = HyperrealNumber([1.0] * 10)
533
+ elif kececi_type == TYPE_BICOMPLEX:
534
+ s_complex = _parse_complex(start_input_raw)
535
+ a_complex = complex(a_float)
536
+ current_value = BicomplexNumber(s_complex, s_complex / 2)
537
+ add_value_typed = BicomplexNumber(a_complex, a_complex / 2)
538
+ ask_unit = BicomplexNumber(complex(1, 1), complex(0.5, 0.5))
539
+ elif kececi_type == TYPE_NEUTROSOPHIC_BICOMPLEX:
540
+ s_complex = _parse_complex(start_input_raw)
541
+ current_value = NeutrosophicBicomplexNumber(s_complex.real, s_complex.imag, 0, 0, 0, 0, 0, 0)
542
+ add_value_typed = NeutrosophicBicomplexNumber(a_float, 0, 0, 0, 0, 0, 0, 0)
543
+ ask_unit = NeutrosophicBicomplexNumber(*([1.0] * 8))
544
+ elif kececi_type == TYPE_QUATERNION:
545
+ current_value = _parse_quaternion(start_input_raw)
546
+ add_value_typed = np.quaternion(a_float, a_float, a_float, a_float)
547
+ ask_unit = np.quaternion(1, 1, 1, 1)
548
+ else:
549
+ raise ValueError(f"Invalid Keçeci Number Type: {kececi_type}")
550
+
551
+ except (ValueError, TypeError) as e:
552
+ print(f"ERROR: Failed to initialize type {kececi_type} with input '{start_input_raw}': {e}")
553
+ return []
554
+
555
+ sequence = [current_value]
556
+ last_divisor_used = None
557
+ ask_counter = 0
558
+
559
+ for _ in range(iterations):
560
+ added_value = current_value + add_value_typed
561
+ sequence.append(added_value)
562
+
563
+ result_value = added_value
564
+ divided_successfully = False
565
+
566
+ primary_divisor = 3 if last_divisor_used == 2 or last_divisor_used is None else 2
567
+ alternative_divisor = 2 if primary_divisor == 3 else 3
568
+
569
+ for divisor in [primary_divisor, alternative_divisor]:
570
+ if _is_divisible(added_value, divisor, kececi_type):
571
+ result_value = added_value // divisor if use_integer_division else added_value / divisor
572
+ last_divisor_used = divisor
573
+ divided_successfully = True
574
+ break
575
+
576
+ if not divided_successfully and is_prime(added_value):
577
+ modified_value = (added_value + ask_unit) if ask_counter == 0 else (added_value - ask_unit)
578
+ ask_counter = 1 - ask_counter
579
+ sequence.append(modified_value)
580
+
581
+ result_value = modified_value
582
+
583
+ for divisor in [primary_divisor, alternative_divisor]:
584
+ if _is_divisible(modified_value, divisor, kececi_type):
585
+ result_value = modified_value // divisor if use_integer_division else modified_value / divisor
586
+ last_divisor_used = divisor
587
+ break
588
+
589
+ sequence.append(result_value)
590
+ current_value = result_value
591
+
592
+ return sequence
593
+
594
+ def print_detailed_report(sequence: List[Any], params: Dict[str, Any]):
595
+ """Generates and prints a detailed report of the sequence results."""
596
+ if not sequence:
597
+ print("\n--- REPORT ---\nSequence could not be generated.")
598
+ return
599
+
600
+ print("\n\n" + "="*50)
601
+ print("--- DETAILED SEQUENCE REPORT ---")
602
+ print("="*50)
603
+
604
+ print("\n[Parameters Used]")
605
+ print(f" - Keçeci Type: {params.get('type_name', 'N/A')} ({params['type_choice']})")
606
+ print(f" - Start Value: '{params['start_val']}'")
607
+ print(f" - Increment: {params['add_val']}")
608
+ print(f" - Keçeci Steps: {params['steps']}")
609
+
610
+ print("\n[Sequence Summary]")
611
+ print(f" - Total Numbers Generated: {len(sequence)}")
612
+
613
+ kpn = find_kececi_prime_number(sequence)
614
+ print(f" - Keçeci Prime Number (KPN): {kpn if kpn is not None else 'Not found'}")
615
+
616
+ print("\n[Sequence Preview]")
617
+ preview_count = min(len(sequence), 30)
618
+ print(f" --- First {preview_count} Numbers ---")
619
+ for i in range(preview_count):
620
+ print(f" {i}: {sequence[i]}")
621
+
622
+ if len(sequence) > preview_count:
623
+ print(f"\n --- Last {preview_count} Numbers ---")
624
+ for i in range(len(sequence) - preview_count, len(sequence)):
625
+ print(f" {i}: {sequence[i]}")
626
+
627
+ print("\n" + "="*50)
628
+
629
+ while True:
630
+ show_all = input("Do you want to print the full sequence? (y/n): ").lower().strip()
631
+ if show_all in ['y', 'n']:
632
+ break
633
+
634
+ if show_all == 'y':
635
+ print("\n--- FULL SEQUENCE ---")
636
+ for i, num in enumerate(sequence):
637
+ print(f"{i}: {num}")
638
+ print("="*50)
639
+
640
+ # ==============================================================================
641
+ # --- HIGH-LEVEL CONTROL FUNCTIONS ---
642
+ # ==============================================================================
643
+
644
+ def get_with_params(kececi_type_choice: int, iterations: int, start_value_raw: str = "0", add_value_base_scalar: float = 9.0) -> List[Any]:
645
+ """Generates Keçeci Numbers with specified parameters."""
646
+ print(f"\n--- Generating Sequence: Type {kececi_type_choice}, Steps {iterations} ---")
647
+ print(f"Start: '{start_value_raw}', Increment: {add_value_base_scalar}")
648
+
649
+ generated_sequence = unified_generator(
650
+ kececi_type_choice, start_value_raw, add_value_base_scalar, iterations
651
+ )
652
+
653
+ if generated_sequence:
654
+ print(f"Generated {len(generated_sequence)} numbers. Preview: {generated_sequence[:30]}...")
655
+ kpn = find_kececi_prime_number(generated_sequence)
656
+ if kpn is not None:
657
+ print(f"Keçeci Prime Number for this sequence: {kpn}")
658
+ else:
659
+ print("No repeating Keçeci Prime Number found.")
660
+ else:
661
+ print("Sequence generation failed.")
662
+
663
+ return generated_sequence
664
+
665
+ def get_interactive() -> Tuple[List[Any], Dict[str, Any]]:
666
+ """Interactively gets parameters from the user to generate a sequence."""
667
+ print("\n--- Keçeci Number Interactive Generator ---")
668
+ print(" 1: Positive Real 2: Negative Real 3: Complex")
669
+ print(" 4: Float 5: Rational 6: Quaternion")
670
+ print(" 7: Neutrosophic 8: Neutro-Complex 9: Hyperreal")
671
+ print(" 10: Bicomplex 11: Neutro-Bicomplex")
672
+
673
+ while True:
674
+ try:
675
+ type_choice = int(input("Select Keçeci Number Type (1-11): "))
676
+ if 1 <= type_choice <= 11:
677
+ break
678
+ print("Invalid type. Please enter a number between 1 and 11.")
679
+ except ValueError:
680
+ print("Invalid input. Please enter a number.")
681
+
682
+ prompts = {
683
+ 1: "Enter positive integer start (e.g., '10'): ",
684
+ 2: "Enter negative integer start (e.g., '-5'): ",
685
+ 3: "Enter complex start (e.g., '3+4j' or '3' for 3+3j): ",
686
+ 4: "Enter float start (e.g., '3.14'): ",
687
+ 5: "Enter rational start (e.g., '7/2' or '5'): ",
688
+ 6: "Enter quaternion (e.g., '1+2i-3j+k' or '2.5'): ",
689
+ 7: "Enter neutrosophic start (e.g., '5+2I' or '7'): ",
690
+ 8: "Enter complex base for neutro-complex (e.g., '1-2j'): ",
691
+ 9: "Enter hyperreal start (e.g., '5+3e' or '10'): ",
692
+ 10: "Enter complex base for bicomplex (e.g., '2+1j'): ",
693
+ 11: "Enter complex base for neutro-bicomplex (e.g., '1+2j'): "
694
+ }
695
+
696
+ start_prompt = prompts.get(type_choice, "Enter starting value: ")
697
+ start_input_val_raw = input(start_prompt)
698
+ add_base_scalar_val = float(input("Enter base scalar increment (e.g., 9.0): "))
699
+ num_kececi_steps = int(input("Enter number of Keçeci steps (e.g., 15): "))
700
+
701
+ sequence = get_with_params(type_choice, num_kececi_steps, start_input_val_raw, add_base_scalar_val)
702
+
703
+ params = {
704
+ "type_choice": type_choice,
705
+ "start_val": start_input_val_raw,
706
+ "add_val": add_base_scalar_val,
707
+ "steps": num_kececi_steps
708
+ }
709
+ return sequence, params
710
+
711
+ # ==============================================================================
712
+ # --- ANALYSIS AND PLOTTING ---
713
+ # ==============================================================================
714
+
715
+ def find_period(sequence: List[Any], min_repeats: int = 3) -> Optional[List[Any]]:
716
+ """
717
+ Checks if the end of a sequence has a repeating cycle (period).
718
+
719
+ Args:
720
+ sequence: The list of numbers to check.
721
+ min_repeats: How many times the cycle must repeat to be considered stable.
722
+
723
+ Returns:
724
+ The repeating cycle as a list if found, otherwise None.
725
+ """
726
+ if len(sequence) < 10: # Çok kısa dizilerde periyot aramak anlamsız
727
+ return None
728
+
729
+ # Olası periyot uzunluklarını dizinin yarısına kadar kontrol et
730
+ for p_len in range(1, len(sequence) // min_repeats):
731
+ # Dizinin sonundan potansiyel döngüyü al
732
+ candidate_cycle = sequence[-p_len:]
733
+
734
+ # Döngünün en az `min_repeats` defa tekrar edip etmediğini kontrol et
735
+ is_periodic = True
736
+ for i in range(1, min_repeats):
737
+ start_index = -(i + 1) * p_len
738
+ end_index = -i * p_len
739
+
740
+ # Dizinin o bölümünü al
741
+ previous_block = sequence[start_index:end_index]
742
+
743
+ # Eğer bloklar uyuşmuyorsa, bu periyot değildir
744
+ if candidate_cycle != previous_block:
745
+ is_periodic = False
746
+ break
747
+
748
+ # Eğer döngü tüm kontrollerden geçtiyse, periyodu bulduk demektir
749
+ if is_periodic:
750
+ return candidate_cycle
751
+
752
+ # Hiçbir periyot bulunamadı
753
+ return None
754
+
755
+ def find_kececi_prime_number(kececi_numbers_list: List[Any]) -> Optional[int]:
756
+ """Finds the Keçeci Prime Number from a generated sequence."""
757
+ if not kececi_numbers_list:
758
+ return None
759
+
760
+ integer_prime_reps = [
761
+ rep for num in kececi_numbers_list
762
+ if is_prime(num) and (rep := _get_integer_representation(num)) is not None
763
+ ]
764
+
765
+ if not integer_prime_reps:
766
+ return None
767
+
768
+ counts = collections.Counter(integer_prime_reps)
769
+ repeating_primes = [(freq, prime) for prime, freq in counts.items() if freq > 1]
770
+ if not repeating_primes:
771
+ return None
772
+
773
+ _, best_prime = max(repeating_primes)
774
+ return best_prime
775
+
776
+ def plot_numbers(sequence: List[Any], title: str = "Keçeci Number Sequence Analysis"):
777
+ """Plots the generated sequence with detailed visualizations for each type."""
778
+ plt.style.use('seaborn-v0_8-whitegrid')
779
+
780
+ if not sequence:
781
+ print("Sequence is empty, nothing to plot.")
782
+ return
783
+
784
+ fig = plt.figure(figsize=(16, 9))
785
+ plt.suptitle(title, fontsize=16, y=0.98)
786
+ first_elem = sequence[0]
787
+
788
+ if isinstance(first_elem, (int, float, Fraction)):
789
+ ax = fig.add_subplot(1, 1, 1)
790
+ ax.plot([float(x) for x in sequence], 'o-', label="Value")
791
+ ax.set_title("Value over Iterations")
792
+ ax.set_xlabel("Index"), ax.set_ylabel("Value"), ax.legend()
793
+
794
+ elif isinstance(first_elem, complex):
795
+ gs = GridSpec(2, 2, figure=fig)
796
+ ax1, ax2, ax3 = fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1]), fig.add_subplot(gs[1, :])
797
+ real_parts, imag_parts = [c.real for c in sequence], [c.imag for c in sequence]
798
+ ax1.plot(real_parts, 'o-', label='Real Part'), ax1.set_title("Real Part"), ax1.legend()
799
+ ax2.plot(imag_parts, 'o-', color='red', label='Imaginary Part'), ax2.set_title("Imaginary Part"), ax2.legend()
800
+ ax3.plot(real_parts, imag_parts, '.-', label='Trajectory')
801
+ ax3.scatter(real_parts[0], imag_parts[0], c='g', s=100, label='Start', zorder=5)
802
+ ax3.scatter(real_parts[-1], imag_parts[-1], c='r', s=100, label='End', zorder=5)
803
+ ax3.set_title("Trajectory in Complex Plane"), ax3.set_xlabel("Real"), ax3.set_ylabel("Imaginary"), ax3.legend(), ax3.axis('equal')
804
+
805
+ elif isinstance(first_elem, np.quaternion):
806
+ gs = GridSpec(2, 1, figure=fig)
807
+ ax1 = fig.add_subplot(gs[0, 0])
808
+ ax2 = fig.add_subplot(gs[1, 0], sharex=ax1)
809
+ ax1.plot([q.w for q in sequence], 'o-', label='w (scalar)'), ax1.plot([q.x for q in sequence], 's--', label='x')
810
+ ax1.plot([q.y for q in sequence], '^--', label='y'), ax1.plot([q.z for q in sequence], 'd--', label='z')
811
+ ax1.set_title("Quaternion Components"), ax1.legend()
812
+ magnitudes = [abs(q) for q in sequence]
813
+ ax2.plot(magnitudes, 'o-', color='purple', label='Magnitude'), ax2.set_title("Magnitude"), ax2.legend(), ax2.set_xlabel("Index")
814
+
815
+ elif isinstance(first_elem, BicomplexNumber):
816
+ gs = GridSpec(2, 2, figure=fig)
817
+ ax1, ax2, ax3, ax4 = fig.add_subplot(gs[0,0]), fig.add_subplot(gs[0,1]), fig.add_subplot(gs[1,0]), fig.add_subplot(gs[1,1])
818
+ z1r, z1i = [x.z1.real for x in sequence], [x.z1.imag for x in sequence]
819
+ z2r, z2i = [x.z2.real for x in sequence], [x.z2.imag for x in sequence]
820
+ ax1.plot(z1r, label='z1.real'), ax1.plot(z1i, label='z1.imag'), ax1.set_title("Component z1"), ax1.legend()
821
+ ax2.plot(z2r, label='z2.real'), ax2.plot(z2i, label='z2.imag'), ax2.set_title("Component z2"), ax2.legend()
822
+ ax3.plot(z1r, z1i, '.-'), ax3.set_title("z1 Trajectory"), ax3.set_xlabel("Real"), ax3.set_ylabel("Imaginary")
823
+ ax4.plot(z2r, z2i, '.-'), ax4.set_title("z2 Trajectory"), ax4.set_xlabel("Real"), ax4.set_ylabel("Imaginary")
824
+
825
+ elif isinstance(first_elem, NeutrosophicNumber):
826
+ gs = GridSpec(1, 2, figure=fig)
827
+ ax1, ax2 = fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1])
828
+ a, b = [x.a for x in sequence], [x.b for x in sequence]
829
+ ax1.plot(a, label='Determinate (a)'), ax1.plot(b, label='Indeterminate (b)'), ax1.set_title("Components"), ax1.legend()
830
+ sc = ax2.scatter(a, b, c=range(len(a)), cmap='viridis')
831
+ ax2.set_title("Trajectory"), ax2.set_xlabel("Determinate"), ax2.set_ylabel("Indeterminate"), fig.colorbar(sc, ax=ax2, label="Iteration")
832
+
833
+ elif isinstance(first_elem, NeutrosophicComplexNumber):
834
+ gs = GridSpec(2, 1, figure=fig)
835
+ ax1, ax2 = fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[1, 0])
836
+ r, i, ind = [x.real for x in sequence], [x.imag for x in sequence], [x.indeterminacy for x in sequence]
837
+ ax1.plot(r, label='Real'), ax1.plot(i, label='Imag'), ax1.plot(ind, label='Indeterminacy', linestyle=':')
838
+ ax1.set_title("Components"), ax1.legend()
839
+ sc = ax2.scatter(r, i, c=ind, cmap='magma', s=20)
840
+ ax2.set_title("Trajectory (colored by Indeterminacy)"), ax2.set_xlabel("Real"), ax2.set_ylabel("Imaginary")
841
+ fig.colorbar(sc, ax=ax2, label='Indeterminacy'), ax2.axis('equal')
842
+
843
+ elif isinstance(first_elem, HyperrealNumber):
844
+ gs = GridSpec(2, 1, figure=fig)
845
+ ax1, ax2 = fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[1, 0])
846
+ num_components = min(len(first_elem.sequence), 4)
847
+ for i in range(num_components):
848
+ ax1.plot([h.sequence[i] for h in sequence], label=f'Comp {i}')
849
+ ax1.set_title("Hyperreal Components"), ax1.legend()
850
+ comp0, comp1 = [h.sequence[0] for h in sequence], [h.sequence[1] for h in sequence]
851
+ sc = ax2.scatter(comp0, comp1, c=range(len(comp0)), cmap='plasma')
852
+ ax2.set_title("Trajectory (C0 vs C1)"), ax2.set_xlabel("C0"), ax2.set_ylabel("C1"), fig.colorbar(sc, ax=ax2, label="Iteration")
853
+
854
+ elif isinstance(first_elem, NeutrosophicBicomplexNumber):
855
+ gs = GridSpec(2, 2, figure=fig)
856
+ ax1, ax2 = fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1])
857
+ ax3, ax4 = fig.add_subplot(gs[1, 0]), fig.add_subplot(gs[1, 1])
858
+ ax1.plot([n.real for n in sequence], [n.imag for n in sequence], '.-'), ax1.set_title("Primary Deterministic")
859
+ ax2.plot([n.neut_real for n in sequence], [n.neut_imag for n in sequence], '.-'), ax2.set_title("Primary Neutrosophic")
860
+ ax3.plot([n.j_real for n in sequence], [n.j_imag for n in sequence], '.-'), ax3.set_title("Secondary Deterministic")
861
+ ax4.plot([n.j_neut_real for n in sequence], [n.j_neut_imag for n in sequence], '.-'), ax4.set_title("Secondary Neutrosophic")
862
+
863
+ else:
864
+ ax = fig.add_subplot(1, 1, 1)
865
+ ax.text(0.5, 0.5, f"Plotting for '{type(first_elem).__name__}' not implemented.", ha='center')
866
+
867
+ plt.tight_layout(rect=[0, 0, 1, 0.96])
868
+
869
+ # ==============================================================================
870
+ # --- MAIN EXECUTION BLOCK ---
871
+ # ==============================================================================
872
+ if __name__ == "__main__":
873
+ print("="*60)
874
+ print(" Keçeci Numbers Module - Demonstration")
875
+ print("="*60)
876
+ print("This script demonstrates the generation of various Keçeci Number types.")
877
+
878
+ # --- Example 1: Interactive Mode ---
879
+ # Uncomment the following lines to run in interactive mode:
880
+ # seq, params = get_interactive()
881
+ # if seq:
882
+ # plot_numbers(seq, title=f"Keçeci Type {params['type_choice']} Sequence")
883
+ # plt.show()
884
+
885
+ # --- Example 2: Programmatic Generation and Plotting ---
886
+ print("\nRunning programmatic tests for all 11 number types...")
887
+
888
+ STEPS = 30
889
+ START_VAL = "2.5"
890
+ ADD_VAL = 3.0
891
+
892
+ all_types = {
893
+ "Positive Real": TYPE_POSITIVE_REAL, "Negative Real": TYPE_NEGATIVE_REAL,
894
+ "Complex": TYPE_COMPLEX, "Float": TYPE_FLOAT, "Rational": TYPE_RATIONAL,
895
+ "Quaternion": TYPE_QUATERNION, "Neutrosophic": TYPE_NEUTROSOPHIC,
896
+ "Neutrosophic Complex": TYPE_NEUTROSOPHIC_COMPLEX, "Hyperreal": TYPE_HYPERREAL,
897
+ "Bicomplex": TYPE_BICOMPLEX, "Neutrosophic Bicomplex": TYPE_NEUTROSOPHIC_BICOMPLEX
898
+ }
899
+
900
+ types_to_plot = [
901
+ "Complex", "Quaternion", "Bicomplex", "Neutrosophic Complex", "Hyperreal"
902
+ ]
903
+
904
+ for name, type_id in all_types.items():
905
+ start = "-5" if type_id == TYPE_NEGATIVE_REAL else "2+3j" if type_id in [TYPE_COMPLEX, TYPE_BICOMPLEX] else START_VAL
906
+
907
+ seq = get_with_params(type_id, STEPS, start, ADD_VAL)
908
+
909
+ if name in types_to_plot and seq:
910
+ plot_numbers(seq, title=f"Demonstration: {name} Keçeci Numbers")
911
+
912
+ print("\n\nDemonstration finished. Plots for selected types are shown.")
913
+ plt.show()