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