kececinumbers 0.1.5__py3-none-any.whl → 0.2.1__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.
@@ -1,628 +1,873 @@
1
- # kececinumbers.py
1
+ ### `kececinumbers.py`
2
2
 
3
- import matplotlib.pyplot as plt
4
- import random
5
- import numpy as np
3
+ # -*- coding: utf-8 -*-
4
+ """
5
+ Keçeci Numbers Module (kececinumbers.py)
6
+
7
+ This module provides a comprehensive framework for generating, analyzing, and
8
+ visualizing Keçeci Numbers across various number systems. It supports 11
9
+ distinct types, from standard integers and complex numbers to more exotic
10
+ constructs like neutrosophic and bicomplex numbers.
11
+
12
+ The core of the module is the `unified_generator`, which implements the
13
+ specific algorithm for creating Keçeci Number sequences. High-level functions
14
+ are available for easy interaction, parameter-based generation, and plotting.
15
+
16
+ Key Features:
17
+ - Generation of 11 types of Keçeci Numbers.
18
+ - A robust, unified algorithm for all number types.
19
+ - Helper functions for mathematical properties like primality and divisibility.
20
+ - Advanced plotting capabilities tailored to each number system.
21
+ - Functions for interactive use or programmatic integration.
22
+ """
23
+
24
+ # --- Standard Library Imports ---
25
+ import collections
6
26
  import math
27
+ import random
28
+ from dataclasses import dataclass
7
29
  from fractions import Fraction
8
- import quaternion # pip install numpy numpy-quaternion
9
- import collections
10
30
 
31
+ # --- Third-Party Imports ---
32
+ import matplotlib.pyplot as plt
33
+ import numpy as np
34
+ import quaternion # Requires: pip install numpy numpy-quaternion
35
+ from matplotlib.gridspec import GridSpec
36
+
37
+ # ==============================================================================
38
+ # --- MODULE CONSTANTS: KEÇECI NUMBER TYPES ---
39
+ # ==============================================================================
40
+ TYPE_POSITIVE_REAL = 1
41
+ TYPE_NEGATIVE_REAL = 2
42
+ TYPE_COMPLEX = 3
43
+ TYPE_FLOAT = 4
44
+ TYPE_RATIONAL = 5
45
+ TYPE_QUATERNION = 6
46
+ TYPE_NEUTROSOPHIC = 7
47
+ TYPE_NEUTROSOPHIC_COMPLEX = 8
48
+ TYPE_HYPERREAL = 9
49
+ TYPE_BICOMPLEX = 10
50
+ TYPE_NEUTROSOPHIC_BICOMPLEX = 11
51
+
52
+ # ==============================================================================
53
+ # --- CUSTOM NUMBER CLASS DEFINITIONS ---
54
+ # ==============================================================================
55
+
56
+ @dataclass
57
+ class NeutrosophicNumber:
58
+ """
59
+ Represents a neutrosophic number of the form a + bI, where I is the
60
+ indeterminate part and I^2 = I.
61
+
62
+ Attributes:
63
+ a (float): The determinate part.
64
+ b (float): The indeterminate part.
65
+ """
66
+ a: float
67
+ b: float
68
+
69
+ def __add__(self, other):
70
+ if isinstance(other, NeutrosophicNumber):
71
+ return NeutrosophicNumber(self.a + other.a, self.b + other.b)
72
+ return NeutrosophicNumber(self.a + other, self.b)
73
+
74
+ def __sub__(self, other):
75
+ if isinstance(other, NeutrosophicNumber):
76
+ return NeutrosophicNumber(self.a - other.a, self.b - other.b)
77
+ return NeutrosophicNumber(self.a - other, self.b)
78
+
79
+ def __mul__(self, other):
80
+ if isinstance(other, NeutrosophicNumber):
81
+ # (a + bI)(c + dI) = ac + (ad + bc + bd)I
82
+ return NeutrosophicNumber(
83
+ self.a * other.a,
84
+ self.a * other.b + self.b * other.a + self.b * other.b
85
+ )
86
+ return NeutrosophicNumber(self.a * other, self.b * other)
87
+
88
+ def __truediv__(self, divisor):
89
+ if isinstance(divisor, (int, float)):
90
+ return NeutrosophicNumber(self.a / divisor, self.b / divisor)
91
+ raise TypeError("Only scalar division is supported for NeutrosophicNumber.")
92
+
93
+ def __str__(self):
94
+ return f"{self.a} + {self.b}I"
95
+
96
+ @dataclass
97
+ class NeutrosophicComplexNumber:
98
+ """
99
+ Represents a neutrosophic-complex number, combining a standard complex number
100
+ (real + imag*j) with an independent level of indeterminacy (I).
101
+
102
+ This object models systems where a value has both a complex-valued state
103
+ (like quantum amplitude) and an associated level of uncertainty or
104
+ unreliability (like quantum decoherence).
105
+
106
+ Attributes:
107
+ real (float): The real part of the deterministic component.
108
+ imag (float): The imaginary part of the deterministic component.
109
+ indeterminacy (float): The coefficient of the indeterminate part, I.
110
+ """
111
+
112
+ def __init__(self, real: float = 0.0, imag: float = 0.0, indeterminacy: float = 0.0):
113
+ """
114
+ Initialises a NeutrosophicComplexNumber.
115
+
116
+ Args:
117
+ real (float): The initial real part. Defaults to 0.0.
118
+ imag (float): The initial imaginary part. Defaults to 0.0.
119
+ indeterminacy (float): The initial indeterminacy level. Defaults to 0.0.
120
+ """
121
+ self.real = float(real)
122
+ self.imag = float(imag)
123
+ self.indeterminacy = float(indeterminacy)
124
+
125
+ def __repr__(self) -> str:
126
+ """
127
+ Returns an unambiguous, developer-friendly representation of the object.
128
+ """
129
+ return f"NeutrosophicComplexNumber(real={self.real}, imag={self.imag}, indeterminacy={self.indeterminacy})"
130
+
131
+ def __str__(self) -> str:
132
+ """
133
+ Returns a user-friendly string representation of the object.
134
+ """
135
+ # Shows a sign for the imaginary part for clarity (e.g., +1.0j, -2.0j)
136
+ return f"({self.real}{self.imag:+}j) + {self.indeterminacy}I"
137
+
138
+ # --- Mathematical Operations ---
139
+
140
+ def __add__(self, other):
141
+ """Adds another number to this one."""
142
+ if isinstance(other, NeutrosophicComplexNumber):
143
+ return NeutrosophicComplexNumber(
144
+ self.real + other.real,
145
+ self.imag + other.imag,
146
+ self.indeterminacy + other.indeterminacy
147
+ )
148
+ # Allows adding a scalar (int/float) to the real part.
149
+ elif isinstance(other, (int, float)):
150
+ return NeutrosophicComplexNumber(self.real + other, self.imag, self.indeterminacy)
151
+ return NotImplemented
152
+
153
+ def __sub__(self, other):
154
+ """Subtracts another number from this one."""
155
+ if isinstance(other, NeutrosophicComplexNumber):
156
+ return NeutrosophicComplexNumber(
157
+ self.real - other.real,
158
+ self.imag - other.imag,
159
+ self.indeterminacy - other.indeterminacy
160
+ )
161
+ elif isinstance(other, (int, float)):
162
+ return NeutrosophicComplexNumber(self.real - other, self.imag, self.indeterminacy)
163
+ return NotImplemented
164
+
165
+ def __mul__(self, other):
166
+ """
167
+ Multiplies this number by another number (scalar, complex, or neutrosophic-complex).
168
+ This is the most critical operation for complex dynamics.
169
+ """
170
+ if isinstance(other, NeutrosophicComplexNumber):
171
+ # (a+bj)*(c+dj) = (ac-bd) + (ad+bc)j
172
+ new_real = self.real * other.real - self.imag * other.imag
173
+ new_imag = self.real * other.imag + self.imag * other.real
174
+
175
+ # The indeterminacy grows based on both original indeterminacies and
176
+ # the magnitude of the deterministic part, creating rich, non-linear behaviour.
177
+ new_indeterminacy = self.indeterminacy + other.indeterminacy + (self.magnitude_sq() * other.indeterminacy)
178
+
179
+ return NeutrosophicComplexNumber(new_real, new_imag, new_indeterminacy)
180
+
181
+ elif isinstance(other, complex):
182
+ # Multiply by a standard Python complex number
183
+ new_real = self.real * other.real - self.imag * other.imag
184
+ new_imag = self.real * other.imag + self.imag * other.real
185
+ # The indeterminacy is unaffected when multiplied by a purely deterministic complex number.
186
+ return NeutrosophicComplexNumber(new_real, new_imag, self.indeterminacy)
187
+
188
+ elif isinstance(other, (int, float)):
189
+ # Multiply by a scalar
190
+ return NeutrosophicComplexNumber(
191
+ self.real * other,
192
+ self.imag * other,
193
+ self.indeterminacy * other
194
+ )
195
+ return NotImplemented
196
+
197
+ def __truediv__(self, divisor):
198
+ """Divides this number by a scalar."""
199
+ if isinstance(divisor, (int, float)):
200
+ if divisor == 0:
201
+ raise ZeroDivisionError("Cannot divide a NeutrosophicComplexNumber by zero.")
202
+ return NeutrosophicComplexNumber(
203
+ self.real / divisor,
204
+ self.imag / divisor,
205
+ self.indeterminacy / divisor
206
+ )
207
+ raise TypeError("Only scalar division is supported for NeutrosophicComplexNumber.")
208
+
209
+ # --- Reversed Mathematical Operations ---
210
+
211
+ def __radd__(self, other):
212
+ """Handles cases like `5 + my_number`."""
213
+ return self.__add__(other)
214
+
215
+ def __rsub__(self, other):
216
+ """Handles cases like `5 - my_number`."""
217
+ if isinstance(other, (int, float)):
218
+ return NeutrosophicComplexNumber(other - self.real, -self.imag, -self.indeterminacy)
219
+ return NotImplemented
220
+
221
+ def __rmul__(self, other):
222
+ """Handles cases like `5 * my_number`."""
223
+ return self.__mul__(other)
224
+
225
+ # --- Unary and Comparison Operations ---
226
+
227
+ def __neg__(self):
228
+ """Returns the negative of the number."""
229
+ return NeutrosophicComplexNumber(-self.real, -self.imag, self.indeterminacy)
230
+
231
+ def __eq__(self, other) -> bool:
232
+ """Checks for equality between two numbers."""
233
+ if not isinstance(other, NeutrosophicComplexNumber):
234
+ return False
235
+ return (self.real == other.real and
236
+ self.imag == other.imag and
237
+ self.indeterminacy == other.indeterminacy)
238
+
239
+ # --- Helper Methods ---
240
+
241
+ def magnitude_sq(self) -> float:
242
+ """Returns the squared magnitude of the deterministic (complex) part."""
243
+ return self.real**2 + self.imag**2
244
+
245
+ def magnitude(self) -> float:
246
+ """Returns the magnitude (modulus or absolute value) of the deterministic part."""
247
+ return math.sqrt(self.magnitude_sq())
248
+
249
+ def deterministic_part(self) -> complex:
250
+ """Returns the deterministic part as a standard Python complex number."""
251
+ return complex(self.real, self.imag)
252
+
253
+
254
+ @dataclass
255
+ class HyperrealNumber:
256
+ """
257
+ Represents a hyperreal number as a sequence of real numbers.
258
+ Operations are performed element-wise on the sequences.
259
+
260
+ Attributes:
261
+ sequence (list[float]): The sequence representing the hyperreal.
262
+ """
263
+ sequence: list
264
+
265
+ def __add__(self, other):
266
+ if isinstance(other, HyperrealNumber):
267
+ return HyperrealNumber([a + b for a, b in zip(self.sequence, other.sequence)])
268
+ raise TypeError("Unsupported operand for +: HyperrealNumber and non-HyperrealNumber.")
269
+
270
+ def __sub__(self, other):
271
+ if isinstance(other, HyperrealNumber):
272
+ return HyperrealNumber([a - b for a, b in zip(self.sequence, other.sequence)])
273
+ raise TypeError("Unsupported operand for -: HyperrealNumber and non-HyperrealNumber.")
274
+
275
+ # --- YENİ EKLENEN DÜZELTME ---
276
+ # --- NEWLY ADDED FIX ---
277
+ def __mul__(self, scalar):
278
+ """Handles multiplication by a scalar (int or float)."""
279
+ if isinstance(scalar, (int, float)):
280
+ return HyperrealNumber([x * scalar for x in self.sequence])
281
+ raise TypeError(f"Unsupported operand for *: HyperrealNumber and {type(scalar).__name__}")
282
+
283
+ def __rmul__(self, scalar):
284
+ """Handles the case where the scalar is on the left (e.g., float * HyperrealNumber)."""
285
+ return self.__mul__(scalar)
286
+ # --- DÜZELTME SONU ---
287
+ # --- END OF FIX ---
288
+
289
+ def __truediv__(self, divisor):
290
+ if isinstance(divisor, (int, float)):
291
+ if divisor == 0:
292
+ raise ZeroDivisionError("Scalar division by zero.")
293
+ return HyperrealNumber([x / divisor for x in self.sequence])
294
+ raise TypeError("Only scalar division is supported.")
295
+
296
+ def __mod__(self, divisor):
297
+ if isinstance(divisor, (int, float)):
298
+ return [x % divisor for x in self.sequence]
299
+ raise TypeError("Modulo operation only supported with a scalar divisor.")
300
+
301
+ def __str__(self):
302
+ return f"Hyperreal({self.sequence[:3]}...)"
303
+
304
+ @dataclass
305
+ class BicomplexNumber:
306
+ """
307
+ Represents a bicomplex number of the form z1 + j*z2, where z1 and z2
308
+ are standard complex numbers, i^2 = -1, and j^2 = -1.
309
+
310
+ Attributes:
311
+ z1 (complex): The first complex component.
312
+ z2 (complex): The second complex component (coefficient of j).
313
+ """
314
+ z1: complex
315
+ z2: complex
316
+
317
+ def __add__(self, other):
318
+ if isinstance(other, BicomplexNumber):
319
+ return BicomplexNumber(self.z1 + other.z1, self.z2 + other.z2)
320
+ raise TypeError("Unsupported operand for +: BicomplexNumber and non-BicomplexNumber.")
321
+
322
+ def __sub__(self, other):
323
+ if isinstance(other, BicomplexNumber):
324
+ return BicomplexNumber(self.z1 - other.z1, self.z2 - other.z2)
325
+ raise TypeError("Unsupported operand for -: BicomplexNumber and non-BicomplexNumber.")
326
+
327
+ def __mul__(self, other):
328
+ if isinstance(other, BicomplexNumber):
329
+ # (z1 + z2j)(w1 + w2j) = (z1w1 - z2w2) + (z1w2 + z2w1)j
330
+ return BicomplexNumber(
331
+ (self.z1 * other.z1) - (self.z2 * other.z2),
332
+ (self.z1 * other.z2) + (self.z2 * other.z1)
333
+ )
334
+ raise TypeError("Unsupported operand for *: BicomplexNumber and non-BicomplexNumber.")
335
+
336
+ def __truediv__(self, scalar):
337
+ if isinstance(scalar, (int, float)):
338
+ return BicomplexNumber(self.z1 / scalar, self.z2 / scalar)
339
+ raise TypeError("Only scalar division is supported.")
340
+
341
+ def __str__(self):
342
+ return f"Bicomplex({self.z1}, {self.z2})"
343
+
344
+ @dataclass
345
+ class NeutrosophicBicomplexNumber:
346
+ """
347
+ Represents a highly complex number with multiple components.
348
+ NOTE: The multiplication implemented here is a simplified, element-wise
349
+ operation for demonstrative purposes and is not mathematically rigorous.
350
+ The true algebraic multiplication is exceedingly complex.
351
+ """
352
+ real: float
353
+ imag: float
354
+ neut_real: float
355
+ neut_imag: float
356
+ j_real: float
357
+ j_imag: float
358
+ j_neut_real: float
359
+ j_neut_imag: float
360
+
361
+ def __add__(self, other):
362
+ if isinstance(other, NeutrosophicBicomplexNumber):
363
+ return NeutrosophicBicomplexNumber(*(a + b for a, b in zip(self.__dict__.values(), other.__dict__.values())))
364
+ raise TypeError("Unsupported operand for +.")
365
+
366
+ def __sub__(self, other):
367
+ if isinstance(other, NeutrosophicBicomplexNumber):
368
+ return NeutrosophicBicomplexNumber(*(a - b for a, b in zip(self.__dict__.values(), other.__dict__.values())))
369
+ raise TypeError("Unsupported operand for -.")
370
+
371
+ def __truediv__(self, scalar):
372
+ if isinstance(scalar, (int, float)):
373
+ return NeutrosophicBicomplexNumber(*(val / scalar for val in self.__dict__.values()))
374
+ raise TypeError("Only scalar division supported.")
375
+
376
+ def __str__(self):
377
+ return f"NeutroBicomplex(r={self.real}, i={self.imag}, Ir={self.neut_real}, ...)"
378
+
379
+
380
+ # ==============================================================================
381
+ # --- HELPER FUNCTIONS ---
382
+ # ==============================================================================
11
383
 
12
- # --- Helper Functions ---
13
384
  def is_prime(n_input):
14
385
  """
15
- Checks if a given number (or its relevant part for complex types)
16
- is prime.
17
- For Rational numbers, its integer part is used.
18
- For Complex/Quaternion numbers, its real/scalar part is used.
386
+ Checks if a given number (or its principal component) is prime.
387
+ Extracts the relevant integer part from various number types for testing.
19
388
  """
20
389
  value_to_check = 0
390
+ # Extract the integer part to check for primality based on type
21
391
  if isinstance(n_input, (int, float)):
22
392
  value_to_check = abs(int(n_input))
23
393
  elif isinstance(n_input, Fraction):
24
- value_to_check = abs(int(n_input)) # Integer part of the Fraction
394
+ value_to_check = abs(int(n_input))
25
395
  elif isinstance(n_input, complex):
26
396
  value_to_check = abs(int(n_input.real))
27
- elif isinstance(n_input, np.quaternion): # numpy-quaternion type check
28
- value_to_check = abs(int(n_input.w)) # Scalar (real) part
29
- else: # Default for other cases
397
+ elif isinstance(n_input, np.quaternion):
398
+ value_to_check = abs(int(n_input.w))
399
+ elif isinstance(n_input, NeutrosophicNumber):
400
+ value_to_check = abs(int(n_input.a))
401
+ elif isinstance(n_input, NeutrosophicComplexNumber):
402
+ value_to_check = abs(int(n_input.real))
403
+ elif isinstance(n_input, HyperrealNumber):
404
+ value_to_check = abs(int(n_input.sequence[0])) if n_input.sequence else 0
405
+ elif isinstance(n_input, BicomplexNumber):
406
+ value_to_check = abs(int(n_input.z1.real))
407
+ elif isinstance(n_input, NeutrosophicBicomplexNumber):
408
+ value_to_check = abs(int(n_input.real))
409
+ else:
30
410
  try:
31
411
  value_to_check = abs(int(n_input))
32
412
  except (ValueError, TypeError):
33
- return False # Consider not prime
413
+ return False
34
414
 
415
+ # Standard primality test algorithm
35
416
  if value_to_check < 2:
36
417
  return False
37
- # Optimize for 2 and even numbers
38
418
  if value_to_check == 2:
39
419
  return True
40
420
  if value_to_check % 2 == 0:
41
421
  return False
42
- # Check only odd divisors up to sqrt(n)
43
- for i in range(3, int(value_to_check**0.5) + 1, 2):
422
+ # Check only odd divisors up to the square root
423
+ for i in range(3, int(math.sqrt(value_to_check)) + 1, 2):
44
424
  if value_to_check % i == 0:
45
425
  return False
46
426
  return True
47
427
 
48
- # --- Main Keçeci Number Generator ---
428
+ def _is_divisible(value, divisor, kececi_type):
429
+ """
430
+ Helper to check divisibility for different number types.
431
+ Returns True if a number is "perfectly divisible" by an integer divisor.
432
+ """
433
+ try:
434
+ if kececi_type in [TYPE_POSITIVE_REAL, TYPE_NEGATIVE_REAL]:
435
+ return value % divisor == 0
436
+ elif kececi_type == TYPE_FLOAT:
437
+ return math.isclose(value % divisor, 0)
438
+ elif kececi_type == TYPE_RATIONAL:
439
+ return (value / divisor).denominator == 1
440
+ elif kececi_type == TYPE_COMPLEX:
441
+ return math.isclose(value.real % divisor, 0) and math.isclose(value.imag % divisor, 0)
442
+ elif kececi_type == TYPE_QUATERNION:
443
+ return all(math.isclose(c % divisor, 0) for c in [value.w, value.x, value.y, value.z])
444
+ elif kececi_type == TYPE_NEUTROSOPHIC:
445
+ return math.isclose(value.a % divisor, 0) and math.isclose(value.b % divisor, 0)
446
+ elif kececi_type == TYPE_NEUTROSOPHIC_COMPLEX:
447
+ return all(math.isclose(c % divisor, 0) for c in [value.real, value.imag, value.indeterminacy])
448
+ elif kececi_type == TYPE_HYPERREAL:
449
+ return all(math.isclose(x % divisor, 0) for x in value.sequence)
450
+ elif kececi_type == TYPE_BICOMPLEX:
451
+ return (_is_divisible(value.z1, divisor, TYPE_COMPLEX) and
452
+ _is_divisible(value.z2, divisor, TYPE_COMPLEX))
453
+ elif kececi_type == TYPE_NEUTROSOPHIC_BICOMPLEX:
454
+ return all(math.isclose(c % divisor, 0) for c in value.__dict__.values())
455
+ except (TypeError, ValueError):
456
+ return False
457
+ return False
458
+
459
+ # ==============================================================================
460
+ # --- CORE GENERATOR ---
461
+ # ==============================================================================
462
+
49
463
  def unified_generator(kececi_type, start_input_raw, add_input_base_scalar, iterations):
50
464
  """
51
- Calculates Unified Keçeci Numbers.
52
- kececi_type: 1 (Positive Real), 2 (Negative Real), 3 (Complex), 4 (Floating-Point),
53
- 5 (Rational), 6 (Quaternion)
54
- start_input_raw: Starting value (as string or number, appropriate for the type)
55
- add_input_base_scalar: Base scalar value for increment (interpreted based on type)
56
- iterations: Number of iterations
465
+ The core engine for generating Keçeci Number sequences of any supported type.
466
+ This version includes robust type conversion to prevent initialization errors.
57
467
  """
58
- sequence = []
468
+ # --- Step 1: Initialization based on Keçeci Type ---
59
469
  current_value = None
60
- _add_value_typed = None # Type-specific add value
470
+ add_value_typed = None
61
471
  ask_unit = None
62
- use_integer_division = False # Generally False, True only for int-based types
63
-
64
- # Set initial value, add value, and ASK unit based on type
65
- if kececi_type == 1: # Positive Real (treated as integer)
66
- current_value = int(start_input_raw)
67
- _add_value_typed = int(add_input_base_scalar)
68
- ask_unit = 1
69
- use_integer_division = True
70
- elif kececi_type == 2: # Negative Real (treated as integer)
71
- current_value = int(start_input_raw)
72
- _add_value_typed = int(add_input_base_scalar) # Usually negative
73
- ask_unit = 1
74
- use_integer_division = True
75
- elif kececi_type == 3: # Complex Numbers
76
- if isinstance(start_input_raw, complex):
77
- start_complex_val = start_input_raw
78
- elif isinstance(start_input_raw, (int, float)):
79
- s_scalar = float(start_input_raw)
80
- start_complex_val = complex(s_scalar, s_scalar)
81
- else:
82
- try:
83
- start_complex_val = complex(str(start_input_raw))
84
- except ValueError:
85
- try:
86
- s_scalar_from_string = float(str(start_input_raw))
87
- start_complex_val = complex(s_scalar_from_string, s_scalar_from_string)
88
- except ValueError:
89
- raise ValueError(f"Cannot convert start_input_raw '{start_input_raw}' to a complex number.")
90
- current_value = start_complex_val
91
- a_scalar_for_add = float(add_input_base_scalar)
92
- _add_value_typed = complex(a_scalar_for_add, a_scalar_for_add) # a+aj
93
- ask_unit = 1 + 1j
94
- elif kececi_type == 4: # Floating-Point Numbers
95
- current_value = float(start_input_raw)
96
- _add_value_typed = float(add_input_base_scalar)
97
- ask_unit = 1.0
98
- elif kececi_type == 5: # Rational Numbers
99
- if isinstance(start_input_raw, Fraction):
100
- current_value = start_input_raw
101
- else:
472
+ use_integer_division = False
473
+
474
+ try:
475
+ # Convert the ADD value once, as it's always a scalar float.
476
+ a_float = float(add_input_base_scalar)
477
+
478
+ # Handle START value conversion properly within each type-specific block.
479
+ if kececi_type in [TYPE_POSITIVE_REAL, TYPE_NEGATIVE_REAL]:
480
+ # Correctly handle float strings like "2.5" by converting to float first.
481
+ s_int = int(float(start_input_raw))
482
+ current_value = s_int
483
+ add_value_typed = int(a_float)
484
+ ask_unit = 1
485
+ use_integer_division = True
486
+
487
+ elif kececi_type == TYPE_FLOAT:
488
+ current_value = float(start_input_raw)
489
+ add_value_typed = a_float
490
+ ask_unit = 1.0
491
+
492
+ elif kececi_type == TYPE_RATIONAL:
493
+ # The Fraction constructor correctly handles strings like "2.5".
102
494
  current_value = Fraction(str(start_input_raw))
103
- _add_value_typed = Fraction(str(add_input_base_scalar))
104
- ask_unit = Fraction(1, 1)
105
- elif kececi_type == 6: # Quaternions
106
- s_val_q_raw = float(start_input_raw) if not isinstance(start_input_raw, np.quaternion) else start_input_raw
107
- a_val_q_scalar = float(add_input_base_scalar)
108
- if isinstance(s_val_q_raw, np.quaternion):
109
- current_value = s_val_q_raw
110
- else:
111
- current_value = np.quaternion(s_val_q_raw, s_val_q_raw, s_val_q_raw, s_val_q_raw)
112
- _add_value_typed = np.quaternion(a_val_q_scalar, a_val_q_scalar, a_val_q_scalar, a_val_q_scalar)
113
- ask_unit = np.quaternion(1, 1, 1, 1)
114
- else:
115
- raise ValueError("Invalid Keçeci Number Type")
495
+ add_value_typed = Fraction(str(add_input_base_scalar))
496
+ ask_unit = Fraction(1, 1)
497
+
498
+ elif kececi_type == TYPE_COMPLEX:
499
+ s_complex = complex(start_input_raw)
500
+ # If input was a plain number (e.g., "2.5"), interpret it as s+sj.
501
+ if s_complex.imag == 0 and 'j' not in str(start_input_raw).lower():
502
+ s_complex = complex(s_complex.real, s_complex.real)
503
+ current_value = s_complex
504
+ add_value_typed = complex(a_float, a_float)
505
+ ask_unit = 1 + 1j
506
+
507
+ elif kececi_type == TYPE_QUATERNION:
508
+ # Explicitly convert the input string to a float before use.
509
+ s_float = float(start_input_raw)
510
+ current_value = np.quaternion(s_float, s_float, s_float, s_float)
511
+ add_value_typed = np.quaternion(a_float, a_float, a_float, a_float)
512
+ ask_unit = np.quaternion(1, 1, 1, 1)
513
+
514
+ elif kececi_type == TYPE_NEUTROSOPHIC:
515
+ s_float = float(start_input_raw)
516
+ current_value = NeutrosophicNumber(s_float, s_float / 2)
517
+ add_value_typed = NeutrosophicNumber(a_float, a_float / 2)
518
+ ask_unit = NeutrosophicNumber(1, 1)
519
+
520
+ elif kececi_type == TYPE_NEUTROSOPHIC_COMPLEX:
521
+ s_float = float(start_input_raw)
522
+ current_value = NeutrosophicComplexNumber(s_float, s_float / 2, s_float / 3)
523
+ add_value_typed = NeutrosophicComplexNumber(a_float, a_float / 2, a_float / 3)
524
+ ask_unit = NeutrosophicComplexNumber(1, 1, 1)
525
+
526
+ elif kececi_type == TYPE_HYPERREAL:
527
+ s_float = float(start_input_raw)
528
+ current_value = HyperrealNumber([s_float / n for n in range(1, 11)])
529
+ add_value_typed = HyperrealNumber([a_float / n for n in range(1, 11)])
530
+ ask_unit = HyperrealNumber([1.0] * 10)
531
+
532
+ elif kececi_type == TYPE_BICOMPLEX:
533
+ s_complex = complex(start_input_raw)
534
+ a_complex = complex(a_float)
535
+ current_value = BicomplexNumber(s_complex, s_complex / 2)
536
+ add_value_typed = BicomplexNumber(a_complex, a_complex / 2)
537
+ ask_unit = BicomplexNumber(complex(1, 1), complex(0.5, 0.5))
538
+
539
+ elif kececi_type == TYPE_NEUTROSOPHIC_BICOMPLEX:
540
+ s_float = float(start_input_raw)
541
+ parts = [s_float / (n + 1) for n in range(8)]
542
+ add_parts = [a_float / (n + 1) for n in range(8)]
543
+ ask_parts = [1.0 / (n + 1) for n in range(8)]
544
+ current_value = NeutrosophicBicomplexNumber(*parts)
545
+ add_value_typed = NeutrosophicBicomplexNumber(*add_parts)
546
+ ask_unit = NeutrosophicBicomplexNumber(*ask_parts)
547
+
548
+ else:
549
+ raise ValueError(f"Invalid Keçeci Number Type: {kececi_type}")
116
550
 
117
- sequence.append(current_value)
118
- last_divisor_used = None
119
- ask_counter = 0
551
+ except (ValueError, TypeError) as e:
552
+ print(f"Error initializing generator for type {kececi_type} with input '{start_input_raw}': {e}")
553
+ return []
120
554
 
121
- actual_iterations_done = 0
122
- while actual_iterations_done < iterations:
123
- added_value = current_value + _add_value_typed
555
+ # --- Step 2: Iteration Loop (This part remains unchanged) ---
556
+ sequence = [current_value]
557
+ last_divisor_used = None
558
+ ask_counter = 0
559
+
560
+ for _ in range(iterations):
561
+ # Rule 1: Add the increment value
562
+ added_value = current_value + add_value_typed
124
563
  sequence.append(added_value)
125
- actual_iterations_done += 1
126
- if actual_iterations_done >= iterations: break
127
-
128
-
129
- value_for_primality_check = added_value
130
564
 
131
- primary_divisor = 3 if last_divisor_used is None or last_divisor_used == 2 else 2
565
+ result_value = added_value
566
+ divided_successfully = False
567
+
568
+ # Rule 2: Attempt Division
569
+ primary_divisor = 3 if last_divisor_used == 2 or last_divisor_used is None else 2
132
570
  alternative_divisor = 2 if primary_divisor == 3 else 3
133
571
 
134
- divided_successfully = False
135
- result_value = None
136
-
137
- for divisor_candidate in [primary_divisor, alternative_divisor]:
138
- can_divide = False
139
- if kececi_type in [1, 2]:
140
- can_divide = (added_value % divisor_candidate == 0)
141
- elif kececi_type == 3:
142
- can_divide = math.isclose(added_value.real % divisor_candidate, 0) and math.isclose(added_value.imag % divisor_candidate, 0)
143
- elif kececi_type == 4:
144
- can_divide = math.isclose(added_value % divisor_candidate, 0) or \
145
- math.isclose(added_value % divisor_candidate, divisor_candidate) # handles floating point precision
146
- elif kececi_type == 5:
147
- if divisor_candidate != 0:
148
- quotient_rational = added_value / divisor_candidate
149
- can_divide = (quotient_rational.denominator == 1)
150
- elif kececi_type == 6:
151
- # For quaternions, typically division is by scalar. Check if all components are divisible.
152
- # Or, as per your current `is_prime`, just check the scalar part.
153
- # Let's stick to the scalar part for consistency with `is_prime` for now.
154
- can_divide = math.isclose(added_value.w % divisor_candidate, 0) and \
155
- math.isclose(added_value.x % divisor_candidate, 0) and \
156
- math.isclose(added_value.y % divisor_candidate, 0) and \
157
- math.isclose(added_value.z % divisor_candidate, 0)
158
-
159
-
160
- if can_divide:
161
- if use_integer_division:
162
- result_value = added_value // divisor_candidate
163
- else:
164
- result_value = added_value / divisor_candidate # For complex, float, rational, quaternion
165
- last_divisor_used = divisor_candidate
572
+ for divisor in [primary_divisor, alternative_divisor]:
573
+ if _is_divisible(added_value, divisor, kececi_type):
574
+ result_value = added_value // divisor if use_integer_division else added_value / divisor
575
+ last_divisor_used = divisor
166
576
  divided_successfully = True
167
- break
168
-
169
- if not divided_successfully:
170
- if is_prime(value_for_primality_check): # is_prime checks the relevant part (e.g., real part)
171
- modified_value = None
172
- if ask_counter == 0:
173
- modified_value = added_value + ask_unit
174
- ask_counter = 1
175
- else:
176
- modified_value = added_value - ask_unit
177
- ask_counter = 0
178
- sequence.append(modified_value)
179
- actual_iterations_done += 1
180
- if actual_iterations_done >= iterations:
181
- result_value = modified_value # End with modified value if it's the last step
577
+ break
578
+
579
+ # Rule 3: Apply ASK Rule if division failed and the number is prime
580
+ if not divided_successfully and is_prime(added_value):
581
+ # Augment or Shrink the value
582
+ modified_value = (added_value + ask_unit) if ask_counter == 0 else (added_value - ask_unit)
583
+ ask_counter = 1 - ask_counter # Flip between 0 and 1
584
+ sequence.append(modified_value)
585
+
586
+ result_value = modified_value # Default to modified value if re-division fails
587
+
588
+ # Re-attempt division on the modified value
589
+ for divisor in [primary_divisor, alternative_divisor]:
590
+ if _is_divisible(modified_value, divisor, kececi_type):
591
+ result_value = modified_value // divisor if use_integer_division else modified_value / divisor
592
+ last_divisor_used = divisor
182
593
  break
183
-
184
-
185
- current_target_for_division_mod = modified_value
186
- divided_after_modification = False
187
- for divisor_candidate_mod in [primary_divisor, alternative_divisor]: # Re-use primary/alternative logic
188
- can_divide_mod = False
189
- if kececi_type in [1, 2]:
190
- can_divide_mod = (current_target_for_division_mod % divisor_candidate_mod == 0)
191
- elif kececi_type == 3:
192
- can_divide_mod = math.isclose(current_target_for_division_mod.real % divisor_candidate_mod, 0) and \
193
- math.isclose(current_target_for_division_mod.imag % divisor_candidate_mod, 0)
194
- elif kececi_type == 4:
195
- can_divide_mod = math.isclose(current_target_for_division_mod % divisor_candidate_mod, 0) or \
196
- math.isclose(current_target_for_division_mod % divisor_candidate_mod, divisor_candidate_mod)
197
- elif kececi_type == 5:
198
- if divisor_candidate_mod != 0:
199
- quotient_rational_mod = current_target_for_division_mod / divisor_candidate_mod
200
- can_divide_mod = (quotient_rational_mod.denominator == 1)
201
- elif kececi_type == 6:
202
- can_divide_mod = math.isclose(current_target_for_division_mod.w % divisor_candidate_mod, 0) and \
203
- math.isclose(current_target_for_division_mod.x % divisor_candidate_mod, 0) and \
204
- math.isclose(current_target_for_division_mod.y % divisor_candidate_mod, 0) and \
205
- math.isclose(current_target_for_division_mod.z % divisor_candidate_mod, 0)
206
-
207
-
208
- if can_divide_mod:
209
- if use_integer_division:
210
- result_value = current_target_for_division_mod // divisor_candidate_mod
211
- else:
212
- result_value = current_target_for_division_mod / divisor_candidate_mod
213
- last_divisor_used = divisor_candidate_mod # Update last_divisor_used
214
- divided_after_modification = True
215
- break
216
- if not divided_after_modification:
217
- result_value = modified_value
218
- else: # Not prime and not divisible
219
- result_value = added_value
220
594
 
221
595
  sequence.append(result_value)
222
- actual_iterations_done += 1
223
- if actual_iterations_done >= iterations: break
224
596
  current_value = result_value
225
597
 
226
- return sequence[:iterations+1] # Ensure correct length, as we add start + iterations*2 steps
598
+ return sequence
227
599
 
600
+ # ==============================================================================
601
+ # --- HIGH-LEVEL CONTROL FUNCTIONS ---
602
+ # ==============================================================================
228
603
 
229
- # --- Control Mechanisms (Exportable Functions) ---
230
- def get_interactive():
604
+ def get_with_params(kececi_type_choice, iterations, start_value_raw="0", add_value_base_scalar=9.0):
231
605
  """
232
- Interactively gets parameters from the user and generates Keçeci Numbers.
606
+ Generates Keçeci Numbers with specified parameters.
233
607
  """
234
- print("Keçeci Number Types:")
235
- print("1: Positive Real Numbers (Integer: e.g., 1)")
236
- print("2: Negative Real Numbers (Integer: e.g., -3)")
237
- print("3: Complex Numbers (e.g., 3+4j)")
238
- print("4: Floating-Point Numbers (e.g., 2.5)")
239
- print("5: Rational Numbers (e.g., 3/2, 5)")
240
- print("6: Quaternions (scalar start input becomes q(s,s,s,s): e.g., 1 or 2.5)")
608
+ print(f"\n--- Generating Sequence: Type {kececi_type_choice}, Steps {iterations} ---")
609
+ print(f"Start: '{start_value_raw}', Increment: {add_value_base_scalar}")
610
+
611
+ generated_sequence = unified_generator(
612
+ kececi_type_choice,
613
+ start_value_raw,
614
+ add_value_base_scalar,
615
+ iterations
616
+ )
241
617
 
242
- while True:
243
- try:
244
- type_choice = int(input("Please select Keçeci Number Type (1-6): "))
245
- if 1 <= type_choice <= 6: break
246
- else: print("Invalid type.")
247
- except ValueError: print("Please enter a numeric value.")
248
-
249
- start_prompt = "Enter the starting number (e.g., 0 or 2.5, complex:3+4j, rational: 3/4, quaternions: 1) : "
250
- if type_choice == 3: start_prompt = "Enter starting complex number (e.g., 3+4j or just 3 for 3+3j): "
251
- elif type_choice == 5: start_prompt = "Enter starting rational (e.g., 7/2 or 5 for 5/1): "
252
-
253
- start_input_val_raw = input(start_prompt)
254
-
255
- while True:
256
- try:
257
- add_base_scalar_val = float(input("Enter the base scalar value for increment (e.g., 9): "))
258
- break
259
- except ValueError: print("Please enter a numeric value.")
260
-
261
- while True:
262
- try:
263
- # Iterations'ı Keçeci adımları olarak düşünelim (her adımda 2 sayı eklenir: added_value, result_value)
264
- num_kececi_steps = int(input("Enter the number of Keçeci steps (positive integer: e.g., 15, generates ~30 numbers): "))
265
- if num_kececi_steps > 0: break
266
- else: print("Number of Keçeci steps must be positive.")
267
- except ValueError: print("Please enter an integer value.")
268
-
269
- generated_sequence = unified_generator(type_choice, start_input_val_raw, add_base_scalar_val, num_kececi_steps)
270
-
271
- # *** YENİ EKLENEN KISIM BAŞLANGICI ***
272
618
  if generated_sequence:
273
- print(f"\nGenerated Keçeci Sequence (first 20 of {len(generated_sequence)}): {generated_sequence[:20]}...")
619
+ print(f"Generated {len(generated_sequence)} numbers. Preview: {generated_sequence[:3]}...")
274
620
  kpn = find_kececi_prime_number(generated_sequence)
275
621
  if kpn is not None:
276
622
  print(f"Keçeci Prime Number for this sequence: {kpn}")
277
623
  else:
278
- print("No Keçeci Prime Number found for this sequence.")
624
+ print("No repeating Keçeci Prime Number found.")
279
625
  else:
280
- print("No sequence generated.")
281
- # *** YENİ EKLENEN KISIM SONU ***
282
-
283
- return generated_sequence # Fonksiyon yine de diziyi döndürmeli
626
+ print("Sequence generation failed.")
627
+
628
+ return generated_sequence
284
629
 
285
- def get_with_params(kececi_type_choice, iterations, start_value_raw="0", add_value_base_scalar=9.0, fixed_params=True, random_range_factor=10):
630
+ def get_interactive():
286
631
  """
287
- Generates Keçeci Numbers with specified or randomized parameters.
288
- If fixed_params is False, start_value_raw and add_value_base_scalar are used as bases for randomization.
289
- random_range_factor influences the range of random values.
632
+ Interactively gets parameters from the user and generates Keçeci Numbers.
290
633
  """
291
- actual_start_raw = start_value_raw
292
- actual_add_base = add_value_base_scalar
293
-
294
- if not fixed_params:
295
- if kececi_type_choice == 1:
296
- actual_start_raw = str(random.randint(0, int(random_range_factor)))
297
- actual_add_base = float(random.randint(1, int(random_range_factor*1.5)))
298
- elif kececi_type_choice == 2:
299
- actual_start_raw = str(random.randint(-int(random_range_factor), 0))
300
- actual_add_base = float(random.randint(-int(random_range_factor*1.5), -1))
301
- elif kececi_type_choice == 3:
302
- re_start = random.uniform(-random_range_factor/2, random_range_factor/2)
303
- im_start = random.uniform(-random_range_factor/2, random_range_factor/2)
304
- actual_start_raw = f"{re_start}{im_start:+}j" # String formatında complex
305
- actual_add_base = random.uniform(1, random_range_factor/2)
306
- elif kececi_type_choice == 4:
307
- actual_start_raw = str(random.uniform(-random_range_factor, random_range_factor))
308
- actual_add_base = random.uniform(0.1, random_range_factor/2)
309
- elif kececi_type_choice == 5:
310
- num = random.randint(-random_range_factor, random_range_factor)
311
- den = random.randint(1, int(random_range_factor/2) if random_range_factor/2 >=1 else 1)
312
- actual_start_raw = f"{num}/{den}"
313
- actual_add_base = float(random.randint(1,random_range_factor))
314
- elif kececi_type_choice == 6:
315
- # Quaternion için başlangıç ve ekleme skaler olsun, unified_generator q(s,s,s,s) yapsın
316
- actual_start_raw = str(random.uniform(-random_range_factor/2, random_range_factor/2))
317
- actual_add_base = random.uniform(1, random_range_factor/2)
318
- else:
319
- if kececi_type_choice == 2 and float(actual_add_base) > 0: # add_value_base_scalar float olabilir
320
- actual_add_base = -abs(float(actual_add_base))
321
-
322
-
323
- generated_sequence = unified_generator(kececi_type_choice, actual_start_raw, actual_add_base, iterations)
634
+ print("\n--- Keçeci Number Interactive Generator ---")
635
+ print(" 1: Positive Real 2: Negative Real 3: Complex")
636
+ print(" 4: Float 5: Rational 6: Quaternion")
637
+ print(" 7: Neutrosophic 8: Neutro-Complex 9: Hyperreal")
638
+ print(" 10: Bicomplex 11: Neutro-Bicomplex")
324
639
 
325
- # *** YENİ EKLENEN KISIM BAŞLANGICI ***
326
- if generated_sequence:
327
- print(f"\nGenerated Keçeci Sequence (using get_with_params, first 20 of {len(generated_sequence)}): {generated_sequence[:20]}...")
328
- kpn = find_kececi_prime_number(generated_sequence)
329
- if kpn is not None:
330
- print(f"Keçeci Prime Number for this sequence: {kpn}")
331
- else:
332
- print("No Keçeci Prime Number found for this sequence.")
333
- else:
334
- print("No sequence generated by get_with_params.")
335
- # *** YENİ EKLENEN KISIM SONU ***
336
-
337
- return generated_sequence
640
+ while True:
641
+ try:
642
+ type_choice = int(input(f"Select Keçeci Number Type (1-11): "))
643
+ if 1 <= type_choice <= 11: break
644
+ else: print("Invalid type. Please enter a number between 1 and 11.")
645
+ except ValueError: print("Invalid input. Please enter a number.")
646
+
647
+ start_prompt = "Enter starting value: "
648
+ if type_choice == TYPE_COMPLEX: start_prompt = "Enter complex start (e.g., '3+4j' or '3' for 3+3j): "
649
+ elif type_choice == TYPE_RATIONAL: start_prompt = "Enter rational start (e.g., '7/2' or '5'): "
650
+ elif type_choice == TYPE_BICOMPLEX: start_prompt = "Enter bicomplex start (complex, e.g., '2+1j'): "
651
+
652
+ start_input_val_raw = input(start_prompt)
653
+ add_base_scalar_val = float(input("Enter base scalar increment (e.g., 9.0): "))
654
+ num_kececi_steps = int(input("Enter number of Keçeci steps (e.g., 15): "))
655
+
656
+ sequence = get_with_params(type_choice, num_kececi_steps, start_input_val_raw, add_base_scalar_val)
657
+ plot_numbers(sequence, f"Keçeci Type {type_choice} Sequence")
658
+ plt.show()
338
659
 
660
+ # ==============================================================================
661
+ # --- ANALYSIS AND PLOTTING ---
662
+ # ==============================================================================
339
663
 
340
- def get_random_type(num_iterations, use_fixed_params_for_selected_type=True,
341
- fixed_start_raw="0", fixed_add_base_scalar=9.0, random_factor=10):
664
+ def find_kececi_prime_number(kececi_numbers_list):
342
665
  """
343
- Generates Keçeci Numbers for a randomly selected type.
666
+ Finds the Keçeci Prime Number from a generated sequence.
667
+
668
+ The Keçeci Prime is the integer representation of the most frequent number
669
+ in the sequence whose principal component is itself prime. Ties in frequency
670
+ are broken by choosing the larger prime number.
344
671
  """
345
- random_type_choice = random.randint(1, 6)
346
- type_names_list = ["Positive Integer", "Negative Integer", "Complex", "Float", "Rational", "Quaternion"]
347
- print(f"\nRandomly selected Keçeci Number Type: {random_type_choice} ({type_names_list[random_type_choice-1]})")
672
+ if not kececi_numbers_list:
673
+ return None
674
+
675
+ # Extract integer representations of numbers that are prime
676
+ integer_prime_reps = []
677
+ for num in kececi_numbers_list:
678
+ if is_prime(num):
679
+ # This logic is duplicated from is_prime to get the value itself
680
+ value = 0
681
+ if isinstance(num, (int, float, Fraction)): value = abs(int(num))
682
+ elif isinstance(num, complex): value = abs(int(num.real))
683
+ elif isinstance(num, np.quaternion): value = abs(int(num.w))
684
+ elif isinstance(num, NeutrosophicNumber): value = abs(int(num.a))
685
+ elif isinstance(num, NeutrosophicComplexNumber): value = abs(int(num.real))
686
+ elif isinstance(num, HyperrealNumber): value = abs(int(num.sequence[0])) if num.sequence else 0
687
+ elif isinstance(num, BicomplexNumber): value = abs(int(num.z1.real))
688
+ elif isinstance(num, NeutrosophicBicomplexNumber): value = abs(int(num.real))
689
+ integer_prime_reps.append(value)
690
+
691
+ if not integer_prime_reps:
692
+ return None
693
+
694
+ # Count frequencies of these prime integers
695
+ counts = collections.Counter(integer_prime_reps)
696
+
697
+ # Find primes that repeat
698
+ repeating_primes = [(freq, prime) for prime, freq in counts.items() if freq > 1]
699
+
700
+ if not repeating_primes:
701
+ return None
348
702
 
349
- # get_with_params fonksiyonu zaten KPN'yi yazdıracak, bu yüzden burada tekrar yazdırmaya gerek yok.
350
- # Sadece get_with_params'ı çağırıyoruz.
351
- generated_sequence = get_with_params(random_type_choice, num_iterations,
352
- start_value_raw=fixed_start_raw,
353
- add_value_base_scalar=fixed_add_base_scalar,
354
- fixed_params=use_fixed_params_for_selected_type,
355
- random_range_factor=random_factor)
356
- return generated_sequence # get_with_params zaten KPN yazdırıyor.
357
-
358
- # --- Plotting Function (can be called from the notebook) ---
359
- def plot_numbers(sequence, title="Keçeci Numbers"):
703
+ # Find the one with the highest frequency, using the prime value as a tie-breaker
704
+ _, best_prime = max(repeating_primes)
705
+ return best_prime
706
+
707
+ def plot_numbers(sequence, title="Keçeci Number Sequence Analysis"):
360
708
  """
361
- Plots the generated Keçeci Number sequence.
709
+ Plots the generated Keçeci Number sequence with appropriate visualizations
710
+ for each number type.
362
711
  """
363
- plt.figure(figsize=(14, 8))
712
+ plt.style.use('seaborn-v0_8-whitegrid')
364
713
 
365
714
  if not sequence:
366
715
  print("Sequence is empty, nothing to plot.")
367
- plt.title(title + " (Empty Sequence)")
368
- plt.text(0.5, 0.5, "Empty Sequence", ha='center', va='center', fontsize=16)
369
- plt.show()
370
716
  return
371
717
 
718
+ fig = plt.figure(figsize=(15, 8))
719
+ plt.suptitle(title, fontsize=16, y=0.98)
372
720
  first_elem = sequence[0]
721
+
722
+ # --- Plotting logic per type ---
723
+
724
+ # CORRECTED: Check for the actual Python types.
725
+ # This correctly handles types 1, 2, and 4 (Positive/Negative Real, Float).
726
+ if isinstance(first_elem, (int, float)):
727
+ ax = fig.add_subplot(1, 1, 1)
728
+ ax.plot([float(x) for x in sequence], 'o-')
729
+ ax.set_title("Value over Iterations")
730
+ ax.set_xlabel("Index"); ax.set_ylabel("Value")
373
731
 
374
- if isinstance(first_elem, np.quaternion):
375
- # Filter out non-quaternion if any mixed types (should not happen with unified_generator)
376
- q_sequence = [q for q in sequence if isinstance(q, np.quaternion)]
377
- if not q_sequence:
378
- print("No quaternion data to plot.")
379
- plt.title(title + " (No Quaternion Data)")
380
- plt.text(0.5, 0.5, "No Quaternion Data", ha='center', va='center', fontsize=16)
381
- plt.show()
382
- return
383
-
384
- w_parts = [q.w for q in q_sequence]
385
- vector_norms = [np.sqrt(q.x**2 + q.y**2 + q.z**2) for q in q_sequence]
386
-
387
- plt.subplot(2, 1, 1)
388
- plt.plot(w_parts, marker='o', linestyle='-', label='w (Scalar Part)')
389
- plt.title(title + " - Quaternion Scalar Part (w)")
390
- plt.xlabel("Index"); plt.ylabel("Value"); plt.grid(True); plt.legend()
391
-
392
- plt.subplot(2, 1, 2)
393
- plt.plot(vector_norms, marker='x', linestyle='--', color='purple', label='Vector Part Norm (|xi+yj+zk|)')
394
- plt.title(title + " - Quaternion Vector Part Norm")
395
- plt.xlabel("Index"); plt.ylabel("Value"); plt.grid(True); plt.legend()
732
+ elif isinstance(first_elem, Fraction):
733
+ ax = fig.add_subplot(1, 1, 1)
734
+ ax.plot([float(x) for x in sequence], 'o-')
735
+ ax.set_title("Value over Iterations (as float)")
736
+ ax.set_xlabel("Index"); ax.set_ylabel("Value")
396
737
 
397
738
  elif isinstance(first_elem, complex):
398
- c_sequence = [c for c in sequence if isinstance(c, complex)]
399
- if not c_sequence:
400
- print("No complex data to plot.")
401
- plt.title(title + " (No Complex Data)")
402
- plt.text(0.5, 0.5, "No Complex Data", ha='center', va='center', fontsize=16)
403
- plt.show()
404
- return
405
-
406
- real_parts = [n.real for n in c_sequence]
407
- imag_parts = [n.imag for n in c_sequence]
739
+ gs = GridSpec(2, 2, figure=fig)
740
+ ax1 = fig.add_subplot(gs[0, 0])
741
+ ax2 = fig.add_subplot(gs[0, 1])
742
+ ax3 = fig.add_subplot(gs[1, :])
743
+
744
+ real_parts = [c.real for c in sequence]
745
+ imag_parts = [c.imag for c in sequence]
746
+
747
+ ax1.plot(real_parts, 'o-', label='Real Part')
748
+ ax1.set_title("Real Part"); ax1.legend()
749
+
750
+ ax2.plot(imag_parts, 'o-', color='red', label='Imaginary Part')
751
+ ax2.set_title("Imaginary Part"); ax2.legend()
752
+
753
+ ax3.plot(real_parts, imag_parts, '.-', label='Trajectory')
754
+ ax3.scatter(real_parts[0], imag_parts[0], c='g', s=100, label='Start', zorder=5)
755
+ ax3.scatter(real_parts[-1], imag_parts[-1], c='r', s=100, label='End', zorder=5)
756
+ ax3.set_title("Trajectory in Complex Plane"); ax3.set_xlabel("Real"); ax3.set_ylabel("Imaginary"); ax3.legend(); ax3.axis('equal')
757
+
758
+ elif isinstance(first_elem, np.quaternion):
759
+ gs = GridSpec(2, 1, figure=fig)
760
+ ax1 = fig.add_subplot(gs[0, 0])
761
+ ax2 = fig.add_subplot(gs[1, 0])
762
+
763
+ ax1.plot([q.w for q in sequence], 'o-', label='w (scalar)')
764
+ ax1.plot([q.x for q in sequence], 's--', label='x')
765
+ ax1.plot([q.y for q in sequence], '^--', label='y')
766
+ ax1.plot([q.z for q in sequence], 'd--', label='z')
767
+ ax1.set_title("Quaternion Components"); ax1.legend()
768
+
769
+ magnitudes = [np.sqrt(q.w**2 + q.x**2 + q.y**2 + q.z**2) for q in sequence]
770
+ ax2.plot(magnitudes, 'o-', color='purple', label='Magnitude')
771
+ ax2.set_title("Quaternion Magnitude"); ax2.legend()
772
+
773
+ elif isinstance(first_elem, BicomplexNumber):
774
+ gs = GridSpec(2, 2, figure=fig)
775
+ ax1 = fig.add_subplot(gs[0, 0]); ax2 = fig.add_subplot(gs[0, 1])
776
+ ax3 = fig.add_subplot(gs[1, 0]); ax4 = fig.add_subplot(gs[1, 1])
777
+
778
+ z1r = [x.z1.real for x in sequence]; z1i = [x.z1.imag for x in sequence]
779
+ z2r = [x.z2.real for x in sequence]; z2i = [x.z2.imag for x in sequence]
780
+
781
+ ax1.plot(z1r, label='z1.real'); ax1.plot(z1i, label='z1.imag')
782
+ ax1.set_title("Component z1"); ax1.legend()
408
783
 
409
- plt.subplot(2, 1, 1)
410
- plt.plot(real_parts, marker='o', linestyle='-', label='Real Part')
411
- plt.title(title + " - Complex Real Part"); plt.xlabel("Index")
412
- plt.ylabel("Value"); plt.grid(True); plt.legend()
784
+ ax2.plot(z2r, label='z2.real'); ax2.plot(z2i, label='z2.imag')
785
+ ax2.set_title("Component z2"); ax2.legend()
413
786
 
414
- plt.subplot(2, 1, 2)
415
- plt.plot(imag_parts, marker='x', linestyle='--', color='red', label='Imaginary Part')
416
- plt.title(title + " - Complex Imaginary Part"); plt.xlabel("Index")
417
- plt.ylabel("Value"); plt.grid(True); plt.legend()
787
+ ax3.plot(z1r, z1i, '.-'); ax3.set_title("z1 in Complex Plane")
788
+ ax4.plot(z2r, z2i, '.-'); ax4.set_title("z2 in Complex Plane")
418
789
 
419
- plt.figure(figsize=(8,8))
420
- plt.plot(real_parts, imag_parts, marker='.', linestyle='-')
421
- if real_parts: # Check if list is not empty
422
- plt.plot(real_parts[0], imag_parts[0], 'go', markersize=10, label='Start')
423
- plt.plot(real_parts[-1], imag_parts[-1], 'ro', markersize=10, label='End')
424
- plt.title(title + " - Trajectory in Complex Plane"); plt.xlabel("Real Axis")
425
- plt.ylabel("Imaginary Axis"); plt.axhline(0, color='black', lw=0.5)
426
- plt.axvline(0, color='black', lw=0.5); plt.grid(True); plt.legend(); plt.axis('equal')
790
+ elif isinstance(first_elem, NeutrosophicNumber):
791
+ gs = GridSpec(1, 2, figure=fig)
792
+ ax1 = fig.add_subplot(gs[0, 0]); ax2 = fig.add_subplot(gs[0, 1])
793
+
794
+ a = [x.a for x in sequence]; b = [x.b for x in sequence]
795
+ ax1.plot(a, label='Determinate (a)'); ax1.plot(b, label='Indeterminate (b)')
796
+ ax1.set_title("Components"); ax1.legend()
797
+
798
+ sc = ax2.scatter(a, b, c=range(len(a)), cmap='viridis')
799
+ ax2.set_title("Determinate vs. Indeterminate"); fig.colorbar(sc, ax=ax2)
427
800
 
428
- elif isinstance(first_elem, Fraction):
429
- f_sequence = [f for f in sequence if isinstance(f, Fraction)]
430
- if not f_sequence:
431
- print("No rational data to plot.")
432
- plt.title(title + " (No Rational Data)")
433
- plt.text(0.5, 0.5, "No Rational Data", ha='center', va='center', fontsize=16)
434
- plt.show()
435
- return
436
- float_sequence = [float(f) for f in f_sequence]
437
- plt.plot(float_sequence, marker='o', linestyle='-')
438
- plt.title(title + " (Rational Numbers - plotted as float)")
439
- plt.xlabel("Index"); plt.ylabel("Value (float)"); plt.grid(True)
801
+ elif isinstance(first_elem, NeutrosophicComplexNumber):
802
+ gs = GridSpec(1, 2, figure=fig)
803
+ ax1 = fig.add_subplot(gs[0, 0]); ax2 = fig.add_subplot(gs[0, 1])
804
+
805
+ r = [x.real for x in sequence]; i = [x.imag for x in sequence]; ind = [x.indeterminacy for x in sequence]
806
+ ax1.plot(r, label='Real'); ax1.plot(i, label='Imag'); ax1.plot(ind, label='Indeterminacy', linestyle=':')
807
+ ax1.set_title("Components"); ax1.legend()
808
+
809
+ sc = ax2.scatter(r, i, c=ind, cmap='magma')
810
+ ax2.set_title("Complex Plane (colored by Indeterminacy)"); fig.colorbar(sc, ax=ax2, label='Indeterminacy')
440
811
 
441
- else: # Integers, Floats, or mixed (if any error in generation)
442
- try:
443
- # Attempt to convert all to float for plotting
444
- numeric_sequence = [float(x) for x in sequence if isinstance(x, (int, float, np.number))]
445
- if not numeric_sequence: # If all were non-numeric after filtering
446
- raise ValueError("No numeric data to plot after filtering.")
447
- plt.plot(numeric_sequence, marker='o', linestyle='-')
448
- except (ValueError, TypeError):
449
- print(f"Warning: Sequence for '{title}' contains non-standard numeric or mixed types. Attempting basic plot.")
450
- # Fallback for truly mixed or unplottable types: plot what you can as numbers
451
- plottable_part = []
452
- for x in sequence:
453
- try: plottable_part.append(float(x))
454
- except: pass # Ignore non-convertible
455
- if plottable_part:
456
- plt.plot(plottable_part, marker='o', linestyle='-')
457
- else:
458
- print("Could not plot any part of the sequence.")
459
- plt.title(title + " (Non-Numeric or Unplottable Data)")
460
- plt.text(0.5, 0.5, "Non-Numeric or Unplottable Data", ha='center', va='center', fontsize=16)
461
-
462
- plt.title(title); plt.xlabel("Index")
463
- plt.ylabel("Value"); plt.grid(True)
812
+ else: # Fallback for Hyperreal, Neutro-Bicomplex, and others
813
+ ax = fig.add_subplot(1, 1, 1)
814
+ ax.text(0.5, 0.5, f"Plotting for type '{type(first_elem).__name__}'\nis not specifically implemented.\nShowing string representation of first 3 elements:\n\n1. {sequence[0]}\n2. {sequence[1]}\n3. {sequence[2]}",
815
+ ha='center', va='center', fontsize=12, bbox=dict(facecolor='lightyellow'))
464
816
 
465
- plt.tight_layout()
466
- # plt.show() # Genellikle notebook'ta %matplotlib inline ile otomatik gösterilir.
467
- # .py script'te bu satırın yorumunu kaldırmak gerekebilir.
817
+ plt.tight_layout(rect=[0, 0, 1, 0.96])
468
818
 
469
- def find_kececi_prime_number(kececi_numbers_list):
470
- """
471
- Verilen Keçeci sayıları listesinden Keçeci Asal Sayısını bulur.
472
- Keçeci Asal Sayısı, listede en sık tekrarlayan (veya reel/skaler kısmı asal olan) sayının
473
- asal tamsayı temsilcisinin en sık tekrarlayanıdır.
474
- Eğer frekanslar eşitse, daha büyük olan asal tamsayı temsilcisi tercih edilir.
475
- """
476
- if not kececi_numbers_list:
477
- # Modül içinde çağrıldığı için print yerine None dönmesi daha sessiz olur.
478
- return None
479
-
480
- integer_prime_representations = []
481
- for num_original in kececi_numbers_list:
482
- if is_prime(num_original):
483
- value_checked = 0
484
- if isinstance(num_original, (int, float)):
485
- value_checked = abs(int(num_original))
486
- elif isinstance(num_original, Fraction):
487
- value_checked = abs(int(num_original))
488
- elif isinstance(num_original, complex):
489
- value_checked = abs(int(num_original.real))
490
- elif isinstance(num_original, np.quaternion):
491
- value_checked = abs(int(num_original.w))
492
- else:
493
- try:
494
- value_checked = abs(int(num_original))
495
- except (ValueError, TypeError):
496
- continue
497
- integer_prime_representations.append(value_checked)
498
-
499
- if not integer_prime_representations:
500
- return None
501
-
502
- counts = collections.Counter(integer_prime_representations)
503
- repeating_primes_info = []
504
- for prime_int_val, freq in counts.items():
505
- if freq > 1: # Sadece tekrarlayan asallar
506
- repeating_primes_info.append((freq, prime_int_val))
819
+ # ==============================================================================
820
+ # --- MAIN EXECUTION BLOCK ---
821
+ # ==============================================================================
822
+ if __name__ == "__main__":
823
+ print("="*60)
824
+ print(" Keçeci Numbers Module - Demonstration")
825
+ print("="*60)
826
+ print(f"This script demonstrates the generation of various Keçeci Number types.")
827
+
828
+ # --- Example 1: Interactive Mode ---
829
+ # To run interactive mode, uncomment the following line:
830
+ # get_interactive()
507
831
 
508
- if not repeating_primes_info:
509
- return None
832
+ # --- Example 2: Programmatic Generation and Plotting ---
833
+ # We will generate a sequence for each type to test the system.
834
+ print("\nRunning programmatic tests for all 11 number types...")
510
835
 
511
- # En yüksek frekansa sahip olanı bul. Frekanslar eşitse,
512
- # max() ikinci elemanı (prime_int_val) kullanarak büyük olanı seçer.
513
- # (frekans, sayı) tuple'ları karşılaştırılır.
514
- try:
515
- best_freq, kececi_prime_integer = max(repeating_primes_info)
516
- except ValueError: # repeating_primes_info boşsa (yukarıdaki if ile yakalanmalı ama ekstra güvence)
517
- return None
836
+ # Test parameters
837
+ STEPS = 15
838
+ START_VAL = "2.5"
839
+ ADD_VAL = 3.0
840
+
841
+ all_types = {
842
+ "Positive Real": TYPE_POSITIVE_REAL,
843
+ "Negative Real": TYPE_NEGATIVE_REAL,
844
+ "Complex": TYPE_COMPLEX,
845
+ "Float": TYPE_FLOAT,
846
+ "Rational": TYPE_RATIONAL,
847
+ "Quaternion": TYPE_QUATERNION,
848
+ "Neutrosophic": TYPE_NEUTROSOPHIC,
849
+ "Neutrosophic Complex": TYPE_NEUTROSOPHIC_COMPLEX,
850
+ "Hyperreal": TYPE_HYPERREAL,
851
+ "Bicomplex": TYPE_BICOMPLEX,
852
+ "Neutrosophic Bicomplex": TYPE_NEUTROSOPHIC_BICOMPLEX
853
+ }
854
+
855
+ # Generate and plot for a few selected types
856
+ types_to_plot = [
857
+ "Complex",
858
+ "Quaternion",
859
+ "Bicomplex",
860
+ "Neutrosophic Complex"
861
+ ]
518
862
 
519
- return kececi_prime_integer
520
-
521
- # Constants for Keçeci Types (makes it easier to use from outside)
522
- TYPE_POSITIVE_REAL = 1
523
- TYPE_NEGATIVE_REAL = 2
524
- TYPE_COMPLEX = 3
525
- TYPE_FLOAT = 4
526
- TYPE_RATIONAL = 5
527
- TYPE_QUATERNION = 6
528
-
529
- # --- DEFINITIONS (as a multiline string, can be accessed as kececinumbers.DEFINITIONS) ---
530
- DEFINITIONS = """
531
- Keçeci NumberS UNIFIED DEFINITION
532
-
533
- A Keçeci Number sequence is derived from a `start_input_raw` and an `add_input_base_scalar`.
534
- These inputs are interpreted according to the selected Keçeci Number Type to become the `current_value` and a type-specific `_add_value_typed`.
535
-
536
- In each "Keçeci step" (which typically adds 2 or 3 numbers to the sequence):
537
- 1. `added_value = current_value + _add_value_typed`.
538
- 2. `added_value` is recorded in the sequence.
539
- 3. A `result_value` is obtained by applying the Division Rule and, if necessary, the ASK Rule to `added_value`.
540
- 4. `result_value` is recorded in the sequence.
541
- 5. `current_value = result_value`.
542
- This process is repeated for the specified `number_of_iterations` (Keçeci steps).
543
-
544
- Division Rule:
545
- * A `last_divisor_used` (2 or 3) is tracked.
546
- * `primary_divisor`: If `last_divisor_used` is nonexistent (first step) or 2, it's 3; if it's 3, it's 2.
547
- * `alternative_divisor`: The other of `primary_divisor` (2 or 3).
548
- * `value_to_check_division`: This is `added_value` (or `modified_value` after ASK).
549
- * The part of this value used for divisibility depends on the number type (e.g., real/scalar part for complex/quaternion, the fraction itself for rational).
550
- * For Complex/Quaternion, all components should be divisible by the integer divisor for perfect division.
551
- * First, division by `primary_divisor` is attempted:
552
- * If `value_to_check_division` (or its relevant part) is "perfectly divisible" by `primary_divisor` (in a type-specific sense, e.g., for rationals, the result is an integer),
553
- then `result_value = value_to_check_division / primary_divisor`. `last_divisor_used = primary_divisor`.
554
- * If unsuccessful, division by `alternative_divisor` is attempted:
555
- * If `value_to_check_division` (or its relevant part) is "perfectly divisible" by `alternative_divisor`,
556
- then `result_value = value_to_check_division / alternative_divisor`. `last_divisor_used = alternative_divisor`.
557
-
558
- Primality and ASK (Augment/Shrink then Check) Rule (if Division Fails):
559
- * `value_for_primality_check`: The part/representation of `added_value` used for primality testing (e.g., integer part of real, real part of complex).
560
- * If `is_prime(value_for_primality_check)` is true:
561
- * An `ask_counter` (0 or 1) is used.
562
- * `ask_unit`: A type-specific unit value (e.g., 1 for real, 1+1j for complex, Fraction(1,1) for rational, quaternion(1,1,1,1) for quaternion).
563
- * If `ask_counter` is 0: `modified_value = added_value + ask_unit`, `ask_counter = 1`.
564
- * If `ask_counter` is 1: `modified_value = added_value - ask_unit`, `ask_counter = 0`.
565
- * `modified_value` is added to the sequence.
566
- * The Division Rule above is re-attempted on this `modified_value`.
567
- * If division is successful, `result_value` is the quotient.
568
- * If unsuccessful, `result_value = modified_value`.
569
- * If `is_prime(value_for_primality_check)` is false (and it wasn't divisible):
570
- * `result_value = added_value`.
571
-
572
- Number Types and Specifics:
573
- 1. Positive Real Numbers (Treated as Integer):
574
- * Start/Increment: Positive integers.
575
- * Division: Integer division (`//`). Perfect divisibility: `% == 0`.
576
- * ASK unit: `1`. Primality: `abs(int(number))`.
577
- 2. Negative Real Numbers (Treated as Integer):
578
- * Start/Increment: Generally negative integers.
579
- * Division: Integer division (`//`). Perfect divisibility: `% == 0`.
580
- * ASK unit: `1`. Primality: `abs(int(number))`.
581
- 3. Complex Numbers (`complex`):
582
- * Start/Increment: Complex numbers. Scalar input `s` is interpreted as `s+sj` for start, and scalar `a` as `a+aj` for increment.
583
- * Division: Complex division (`/`). Perfect divisibility: Both real and imaginary parts are `math.isclose(part % integer_divisor, 0)`.
584
- * ASK unit: `1+1j`. Primality: `abs(int(number.real))`.
585
- 4. Floating-Point Numbers (Treated as Float):
586
- * Start/Increment: Decimal numbers.
587
- * Division: Float division (`/`). Perfect divisibility: `math.isclose(number % integer_divisor, 0)`.
588
- * ASK unit: `1.0`. Primality: `abs(int(number))`.
589
- 5. Rational Numbers (`fractions.Fraction`):
590
- * Start/Increment: Fractions. Scalar input `s` is interpreted as `Fraction(s,1)`.
591
- * Division: Fraction division (`/`). Perfect divisibility: `(fraction / integer_divisor).denominator == 1`.
592
- * ASK unit: `Fraction(1,1)`. Primality: `abs(int(fraction))`.
593
- 6. Quaternions (`numpy.quaternion`):
594
- * Start/Increment: Quaternions. Scalar input `s` is interpreted as `q(s,s,s,s)` for start, and scalar `a` as `q(a,a,a,a)` for increment.
595
- * Division: Quaternion division (`/`). Perfect divisibility: All w,x,y,z parts are `math.isclose(part % integer_divisor, 0)`.
596
- * ASK unit: `quaternion(1,1,1,1)`. Primality: `abs(int(number.w))`.
597
- """
598
-
863
+ for name, type_id in all_types.items():
864
+ # Adjust start/add values for specific types if needed
865
+ start = "-5" if type_id == TYPE_NEGATIVE_REAL else "2+3j" if type_id in [TYPE_COMPLEX, TYPE_BICOMPLEX] else START_VAL
866
+
867
+ seq = get_with_params(type_id, STEPS, start, ADD_VAL)
868
+
869
+ if name in types_to_plot and seq:
870
+ plot_numbers(seq, title=f"Demonstration: {name} Keçeci Numbers")
599
871
 
600
- if __name__ == "__main__":
601
- print("Keçeci Numbers Module Loaded.")
602
- print("This module provides functions to generate and plot Keçeci Numbers.")
603
- print("Example: Use 'import kececinumbers as kn' in your script/notebook.")
604
- print("\nAvailable functions:")
605
- print("- kn.get_interactive()")
606
- print("- kn.get_with_params(kececi_type, iterations, ...)")
607
- print("- kn.get_random_type(iterations, ...)")
608
- print("- kn.plot_numbers(sequence, title)")
609
- print("- kn.find_kececi_prime_number(sequence)") # Explicitly list it
610
- print("- kn.unified_generator(...) (low-level)")
611
- print("\nAccess definitions by printing kn.DEFINITIONS") # Changed to instruction
612
- print("\nAccess type constants like: kn.TYPE_COMPLEX")
613
-
614
- print("\nRunning a quick test for Complex numbers (fixed params, 5 Keçeci steps):")
615
- # `iterations` in get_with_params is number of Keçeci steps
616
- test_seq = get_with_params(TYPE_COMPLEX, 5, start_value_raw="1+1j", add_value_base_scalar=2.0, fixed_params=True)
617
- # KPN will be printed by get_with_params
618
-
619
- # Example of calling find_kececi_prime_number directly if needed
620
- # if test_seq:
621
- # kpn_direct = find_kececi_prime_number(test_seq)
622
- # if kpn_direct is not None:
623
- # print(f"Direct call to find_kececi_prime_number result: {kpn_direct}")
624
- # else:
625
- # print("Direct call: No Keçeci Prime Number found.")
626
-
627
- # print("\nRunning a quick test for Negative Integers (10 Keçeci steps):")
628
- # test_seq_neg = get_random_type(num_iterations=10) # KPN will be printed
872
+ print("\n\nDemonstration finished. Plots for selected types are shown.")
873
+ plt.show()