kececinumbers 0.1.0__py3-none-any.whl → 0.1.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,475 +1,475 @@
1
- # kececinumbers.py
2
-
3
- import matplotlib.pyplot as plt
4
- import random
5
- import numpy as np
6
- import math
7
- from fractions import Fraction
8
- import quaternion # pip install numpy numpy-quaternion
9
-
10
-
11
- # --- Helper Functions ---
12
- def is_prime(n_input):
13
- """
14
- Checks if a given number (or its relevant part for complex types)
15
- is prime.
16
- For Rational numbers, its integer part is used.
17
- For Complex/Quaternion numbers, its real/scalar part is used.
18
- """
19
- value_to_check = 0
20
- if isinstance(n_input, (int, float)):
21
- value_to_check = abs(int(n_input))
22
- elif isinstance(n_input, Fraction):
23
- value_to_check = abs(int(n_input)) # Integer part of the Fraction
24
- elif isinstance(n_input, complex):
25
- value_to_check = abs(int(n_input.real))
26
- elif isinstance(n_input, np.quaternion): # numpy-quaternion type check
27
- value_to_check = abs(int(n_input.w)) # Scalar (real) part
28
- else: # Default for other cases
29
- try:
30
- value_to_check = abs(int(n_input))
31
- except (ValueError, TypeError):
32
- return False # Consider not prime
33
-
34
- if value_to_check < 2:
35
- return False
36
- for i in range(2, int(value_to_check**0.5) + 1):
37
- if value_to_check % i == 0:
38
- return False
39
- return True
40
-
41
- # --- Main Keçeci Number Generator ---
42
- def unified_generator(kececi_type, start_input_raw, add_input_base_scalar, iterations):
43
- """
44
- Calculates Unified Keçeci Numbers.
45
- kececi_type: 1 (Positive Real), 2 (Negative Real), 3 (Complex), 4 (Floating-Point),
46
- 5 (Rational), 6 (Quaternion)
47
- start_input_raw: Starting value (as string or number, appropriate for the type)
48
- add_input_base_scalar: Base scalar value for increment (interpreted based on type)
49
- iterations: Number of iterations
50
- """
51
- sequence = []
52
- current_value = None
53
- _add_value_typed = None # Type-specific add value
54
- ask_unit = None
55
- use_integer_division = False # Generally False, True only for int-based types
56
-
57
- # Set initial value, add value, and ASK unit based on type
58
- if kececi_type == 1: # Positive Real (treated as integer)
59
- current_value = int(start_input_raw)
60
- _add_value_typed = int(add_input_base_scalar)
61
- ask_unit = 1
62
- use_integer_division = True
63
- elif kececi_type == 2: # Negative Real (treated as integer)
64
- current_value = int(start_input_raw)
65
- _add_value_typed = int(add_input_base_scalar) # Usually negative
66
- ask_unit = 1
67
- use_integer_division = True
68
- elif kececi_type == 3: # Complex Numbers
69
- # Adım 1: start_input_raw'ı bir complex sayıya dönüştür
70
- if isinstance(start_input_raw, complex):
71
- # Zaten bir complex sayı ise, doğrudan kullan
72
- start_complex_val = start_input_raw
73
- elif isinstance(start_input_raw, (int, float)):
74
- # Eğer int veya float ise, s+sj yap (kullanıcının beklentisi buysa)
75
- s_scalar = float(start_input_raw)
76
- start_complex_val = complex(s_scalar, s_scalar)
77
- else: # String ise (örn: "1+2j", "3", "3.0")
78
- try:
79
- # Doğrudan complex sayı string'i mi diye dene (örn: "1+2j")
80
- start_complex_val = complex(str(start_input_raw))
81
- except ValueError:
82
- # Eğer complex() başarısız olduysa, skaler bir sayı string'i olabilir (örn: "3")
83
- # Bu durumda s+sj yapalım
84
- try:
85
- s_scalar_from_string = float(str(start_input_raw))
86
- start_complex_val = complex(s_scalar_from_string, s_scalar_from_string)
87
- except ValueError:
88
- raise ValueError(f"Cannot convert start_input_raw '{start_input_raw}' to a complex number.")
89
-
90
- current_value = start_complex_val
91
-
92
- # Adım 2: add_input_base_scalar'dan _add_value_typed oluştur
93
- a_scalar_for_add = float(add_input_base_scalar)
94
- _add_value_typed = complex(a_scalar_for_add, a_scalar_for_add) # a+aj
95
-
96
- ask_unit = 1 + 1j
97
- # use_integer_division = False (Zaten varsayılan)
98
- elif kececi_type == 4: # Floating-Point Numbers
99
- current_value = float(start_input_raw)
100
- _add_value_typed = float(add_input_base_scalar)
101
- ask_unit = 1.0
102
- elif kececi_type == 5: # Rational Numbers
103
- if isinstance(start_input_raw, Fraction):
104
- current_value = start_input_raw
105
- else: # String ("3/2", "5") or number (5 -> 5/1)
106
- current_value = Fraction(str(start_input_raw))
107
- _add_value_typed = Fraction(str(add_input_base_scalar)) # Convert scalar base to Fraction
108
- ask_unit = Fraction(1, 1)
109
- elif kececi_type == 6: # Quaternions
110
- s_val_q_raw = float(start_input_raw) if not isinstance(start_input_raw, np.quaternion) else start_input_raw
111
- a_val_q_scalar = float(add_input_base_scalar)
112
- if isinstance(s_val_q_raw, np.quaternion):
113
- current_value = s_val_q_raw
114
- else: # From scalar, make q(s,s,s,s)
115
- current_value = np.quaternion(s_val_q_raw, s_val_q_raw, s_val_q_raw, s_val_q_raw)
116
- _add_value_typed = np.quaternion(a_val_q_scalar, a_val_q_scalar, a_val_q_scalar, a_val_q_scalar)
117
- ask_unit = np.quaternion(1, 1, 1, 1) # Like (1+1i+1j+1k)
118
- else:
119
- raise ValueError("Invalid Keçeci Number Type")
120
-
121
- sequence.append(current_value)
122
- last_divisor_used = None # Last divisor used (2 or 3)
123
- ask_counter = 0 # 0: first time +unit, 1: second time -unit
124
-
125
- for _ in range(iterations):
126
- added_value = current_value + _add_value_typed
127
- sequence.append(added_value)
128
-
129
- value_for_primality_check = added_value
130
-
131
- primary_divisor = 3 if last_divisor_used is None or last_divisor_used == 2 else 2
132
- alternative_divisor = 2 if primary_divisor == 3 else 3
133
-
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)
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)
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
- can_divide = math.isclose(added_value.w % divisor_candidate, 0)
152
-
153
- if can_divide:
154
- if use_integer_division:
155
- result_value = added_value // divisor_candidate
156
- else:
157
- result_value = added_value / divisor_candidate
158
- last_divisor_used = divisor_candidate
159
- divided_successfully = True
160
- break
161
-
162
- if not divided_successfully:
163
- if is_prime(value_for_primality_check):
164
- modified_value = None
165
- if ask_counter == 0:
166
- modified_value = added_value + ask_unit
167
- ask_counter = 1
168
- else:
169
- modified_value = added_value - ask_unit
170
- ask_counter = 0
171
- sequence.append(modified_value)
172
-
173
- current_target_for_division_mod = modified_value
174
- divided_after_modification = False
175
- for divisor_candidate_mod in [primary_divisor, alternative_divisor]:
176
- can_divide_mod = False
177
- if kececi_type in [1, 2]:
178
- can_divide_mod = (current_target_for_division_mod % divisor_candidate_mod == 0)
179
- elif kececi_type == 3:
180
- can_divide_mod = math.isclose(current_target_for_division_mod.real % divisor_candidate_mod, 0)
181
- elif kececi_type == 4:
182
- can_divide_mod = math.isclose(current_target_for_division_mod % divisor_candidate_mod, 0) or \
183
- math.isclose(current_target_for_division_mod % divisor_candidate_mod, divisor_candidate_mod)
184
- elif kececi_type == 5:
185
- if divisor_candidate_mod != 0:
186
- quotient_rational_mod = current_target_for_division_mod / divisor_candidate_mod
187
- can_divide_mod = (quotient_rational_mod.denominator == 1)
188
- elif kececi_type == 6:
189
- can_divide_mod = math.isclose(current_target_for_division_mod.w % divisor_candidate_mod, 0)
190
-
191
- if can_divide_mod:
192
- if use_integer_division:
193
- result_value = current_target_for_division_mod // divisor_candidate_mod
194
- else:
195
- result_value = current_target_for_division_mod / divisor_candidate_mod
196
- last_divisor_used = divisor_candidate_mod
197
- divided_after_modification = True
198
- break
199
- if not divided_after_modification:
200
- result_value = modified_value
201
- else:
202
- result_value = added_value
203
-
204
- sequence.append(result_value)
205
- current_value = result_value
206
-
207
- return sequence
208
-
209
- # --- Control Mechanisms (Exportable Functions) ---
210
- def get_interactive():
211
- """
212
- Interactively gets parameters from the user and generates Keçeci Numbers.
213
- """
214
- print("Keçeci Number Types:")
215
- print("1: Positive Real Numbers (Integer: e.g., 1)")
216
- print("2: Negative Real Numbers (Integer: e.g., -3)")
217
- print("3: Complex Numbers (e.g., 3+4j)")
218
- print("4: Floating-Point Numbers (e.g., 2.5)")
219
- print("5: Rational Numbers (e.g., 3/2, 5)")
220
- print("6: Quaternions (scalar start input becomes q(s,s,s,s): e.g., 1 or 2.5)")
221
-
222
- while True:
223
- try:
224
- type_choice = int(input("Please select Keçeci Number Type (1-6): "))
225
- if 1 <= type_choice <= 6: break
226
- else: print("Invalid type.")
227
- except ValueError: print("Please enter a numeric value.")
228
-
229
- start_prompt = "Enter the starting number (e.g., 0 or 2.5, complex:3+4j, rational: 3/4, quaternions: 1) : "
230
- if type_choice == 3: start_prompt += "(e.g., 3+4j or just 3): "
231
- elif type_choice == 5: start_prompt += "(e.g., 7/2 or 5): "
232
-
233
- start_input_val_raw = input(start_prompt)
234
-
235
- while True:
236
- try:
237
- add_base_scalar_val = float(input("Enter the base scalar value for increment (e.g., 9): "))
238
- break
239
- except ValueError: print("Please enter a numeric value.")
240
-
241
- while True:
242
- try:
243
- num_iterations = int(input("Enter the number of iterations (positive integer: e.g., 30): "))
244
- if num_iterations > 0: break
245
- else: print("Number of iterations must be positive.")
246
- except ValueError: print("Please enter an integer value.")
247
-
248
- return unified_generator(type_choice, start_input_val_raw, add_base_scalar_val, num_iterations)
249
-
250
- 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):
251
- """
252
- Generates Keçeci Numbers with specified or randomized parameters.
253
- If fixed_params is False, start_value_raw and add_value_base_scalar are used as bases for randomization.
254
- random_range_factor influences the range of random values.
255
- """
256
- actual_start_raw = start_value_raw
257
- actual_add_base = add_value_base_scalar
258
-
259
- if not fixed_params:
260
- # Basic randomization logic, can be expanded
261
- if kececi_type_choice == 1: # Positive
262
- actual_start_raw = str(random.randint(0, int(random_range_factor)))
263
- actual_add_base = float(random.randint(1, int(random_range_factor*1.5)))
264
- elif kececi_type_choice == 2: # Negative
265
- actual_start_raw = str(random.randint(-int(random_range_factor), 0))
266
- actual_add_base = float(random.randint(-int(random_range_factor*1.5), -1))
267
- elif kececi_type_choice == 3: # Complex
268
- actual_start_raw = str(random.uniform(-random_range_factor/2, random_range_factor/2))
269
- actual_add_base = random.uniform(1, random_range_factor/2)
270
- elif kececi_type_choice == 4: # Float
271
- actual_start_raw = str(random.uniform(-random_range_factor, random_range_factor))
272
- actual_add_base = random.uniform(0.1, random_range_factor/2)
273
- elif kececi_type_choice == 5: # Rational
274
- num = random.randint(-random_range_factor, random_range_factor)
275
- den = random.randint(1, int(random_range_factor/2) if random_range_factor/2 >=1 else 1)
276
- actual_start_raw = f"{num}/{den}"
277
- actual_add_base = float(random.randint(1,random_range_factor))
278
- elif kececi_type_choice == 6: # Quaternion
279
- actual_start_raw = str(random.uniform(-random_range_factor/2, random_range_factor/2))
280
- actual_add_base = random.uniform(1, random_range_factor/2)
281
- else: # Fixed parameters, but adjust for negative type if default add is positive
282
- if kececi_type_choice == 2 and actual_add_base > 0:
283
- actual_add_base = -actual_add_base
284
-
285
-
286
- return unified_generator(kececi_type_choice, actual_start_raw, actual_add_base, iterations)
287
-
288
-
289
- def get_random_type(num_iterations, use_fixed_params_for_selected_type=True,
290
- fixed_start_raw="0", fixed_add_base_scalar=9.0, random_factor=10):
291
- """
292
- Generates Keçeci Numbers for a randomly selected type.
293
- """
294
- random_type_choice = random.randint(1, 6)
295
- type_names_list = ["Positive Integer", "Negative Integer", "Complex", "Float", "Rational", "Quaternion"]
296
- print(f"Randomly selected Keçeci Number Type: {random_type_choice} ({type_names_list[random_type_choice-1]})")
297
-
298
- return get_with_params(random_type_choice, num_iterations,
299
- start_value_raw=fixed_start_raw,
300
- add_value_base_scalar=fixed_add_base_scalar,
301
- fixed_params=use_fixed_params_for_selected_type,
302
- random_range_factor=random_factor)
303
-
304
- # --- Plotting Function (can be called from the notebook) ---
305
- def plot_numbers(sequence, title="Keçeci Numbers"):
306
- """
307
- Plots the generated Keçeci Number sequence.
308
- """
309
- plt.figure(figsize=(14, 8))
310
-
311
- if not sequence:
312
- print("Sequence is empty, nothing to plot.")
313
- return
314
-
315
- first_elem = sequence[0]
316
-
317
- if isinstance(first_elem, np.quaternion):
318
- w_parts = [q.w for q in sequence]
319
- # x_parts = [q.x for q in sequence] # Optional
320
- # y_parts = [q.y for q in sequence] # Optional
321
- # z_parts = [q.z for q in sequence] # Optional
322
- vector_norms = [np.sqrt(q.x**2 + q.y**2 + q.z**2) for q in sequence]
323
-
324
- plt.subplot(2, 1, 1)
325
- plt.plot(w_parts, marker='o', linestyle='-', label='w (Scalar Part)')
326
- plt.title(title + " - Quaternion Scalar Part (w)")
327
- plt.xlabel("Index"); plt.ylabel("Value"); plt.grid(True); plt.legend()
328
-
329
- plt.subplot(2, 1, 2)
330
- plt.plot(vector_norms, marker='x', linestyle='--', color='purple', label='Vector Part Norm (|xi+yj+zk|)')
331
- plt.title(title + " - Quaternion Vector Part Norm")
332
- plt.xlabel("Index"); plt.ylabel("Value"); plt.grid(True); plt.legend()
333
-
334
- elif isinstance(first_elem, complex):
335
- real_parts = [n.real for n in sequence]
336
- imag_parts = [n.imag for n in sequence]
337
-
338
- plt.subplot(2, 1, 1)
339
- plt.plot(real_parts, marker='o', linestyle='-', label='Real Part')
340
- plt.title(title + " - Complex Real Part"); plt.xlabel("Index")
341
- plt.ylabel("Value"); plt.grid(True); plt.legend()
342
-
343
- plt.subplot(2, 1, 2)
344
- plt.plot(imag_parts, marker='x', linestyle='--', color='red', label='Imaginary Part')
345
- plt.title(title + " - Complex Imaginary Part"); plt.xlabel("Index")
346
- plt.ylabel("Value"); plt.grid(True); plt.legend()
347
-
348
- plt.figure(figsize=(8,8))
349
- plt.plot(real_parts, imag_parts, marker='.', linestyle='-')
350
- if real_parts:
351
- plt.plot(real_parts[0], imag_parts[0], 'go', markersize=8, label='Start')
352
- plt.plot(real_parts[-1], imag_parts[-1], 'ro', markersize=8, label='End')
353
- plt.title(title + " - Trajectory in Complex Plane"); plt.xlabel("Real Axis")
354
- plt.ylabel("Imaginary Axis"); plt.axhline(0, color='black', lw=0.5)
355
- plt.axvline(0, color='black', lw=0.5); plt.grid(True); plt.legend(); plt.axis('equal')
356
-
357
- elif isinstance(first_elem, Fraction):
358
- float_sequence = [float(f) for f in sequence]
359
- plt.plot(float_sequence, marker='o', linestyle='-')
360
- plt.title(title + " (Rational Numbers - plotted as float)")
361
- plt.xlabel("Index"); plt.ylabel("Value (float)"); plt.grid(True)
362
-
363
- else:
364
- try:
365
- numeric_sequence = np.array(sequence, dtype=float)
366
- plt.plot(numeric_sequence, marker='o', linestyle='-')
367
- except ValueError:
368
- print("Sequence contains non-plottable values. Plotting only numeric ones.")
369
- plt.plot([x for x in sequence if isinstance(x, (int, float))], marker='o', linestyle='-')
370
- plt.title(title); plt.xlabel("Index")
371
- plt.ylabel("Value"); plt.grid(True)
372
-
373
- plt.tight_layout()
374
- plt.show()
375
-
376
- # --- DEFINITIONS (as a multiline string, can be accessed as kececinumbers.DEFINITIONS) ---
377
- DEFINITIONS = """
378
- Keçeci NumberS UNIFIED DEFINITION
379
-
380
- A Keçeci Number sequence is derived from a `start_input_raw` and an `add_input_base_scalar`.
381
- These inputs are interpreted according to the selected Keçeci Number Type to become the `current_value` and a type-specific `_add_value_typed`.
382
-
383
- In each step:
384
- 1. `added_value = current_value + _add_value_typed`.
385
- 2. `added_value` is recorded in the sequence.
386
- 3. A `result_value` is obtained by applying the Division Rule and, if necessary, the ASK Rule to `added_value`.
387
- 4. `result_value` is recorded in the sequence.
388
- 5. `current_value = result_value`.
389
- This process is repeated for the specified `number_of_iterations`.
390
-
391
- Division Rule:
392
- * A `last_divisor_used` (2 or 3) is tracked.
393
- * `primary_divisor`: If `last_divisor_used` is nonexistent (first step) or 2, it's 3; if it's 3, it's 2.
394
- * `alternative_divisor`: The other of `primary_divisor` (2 or 3).
395
- * `value_to_check_division`: This is `added_value` (or `modified_value` after ASK).
396
- * 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).
397
- * First, division by `primary_divisor` is attempted:
398
- * 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),
399
- then `result_value = value_to_check_division / primary_divisor`. `last_divisor_used = primary_divisor`.
400
- * If unsuccessful, division by `alternative_divisor` is attempted:
401
- * If `value_to_check_division` (or its relevant part) is "perfectly divisible" by `alternative_divisor`,
402
- then `result_value = value_to_check_division / alternative_divisor`. `last_divisor_used = alternative_divisor`.
403
-
404
- Primality and ASK (Augment/Shrink then Check) Rule (if Division Fails):
405
- * `value_for_primality_check`: The part/representation of `added_value` used for primality testing (e.g., integer part, real part).
406
- * If `is_prime(value_for_primality_check)` is true:
407
- * An `ask_counter` (0 or 1) is used.
408
- * `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).
409
- * If `ask_counter` is 0: `modified_value = added_value + ask_unit`, `ask_counter = 1`.
410
- * If `ask_counter` is 1: `modified_value = added_value - ask_unit`, `ask_counter = 0`.
411
- * `modified_value` is added to the sequence.
412
- * The Division Rule above is re-attempted on this `modified_value`.
413
- * If division is successful, `result_value` is the quotient.
414
- * If unsuccessful, `result_value = modified_value`.
415
- * If `is_prime(value_for_primality_check)` is false (and it wasn't divisible):
416
- * `result_value = added_value`.
417
-
418
- Number Types and Specifics:
419
- 1. Positive Real Numbers (Treated as Integer):
420
- * Start/Increment: Positive integers.
421
- * Division: Integer division (`//`). Perfect divisibility: `% == 0`.
422
- * ASK unit: `1`. Primality: `abs(int(number))`.
423
- 2. Negative Real Numbers (Treated as Integer):
424
- * Start/Increment: Generally negative integers.
425
- * Division: Integer division (`//`). Perfect divisibility: `% == 0`.
426
- * ASK unit: `1`. Primality: `abs(int(number))`.
427
- 3. Complex Numbers (`complex`):
428
- * Start/Increment: Complex numbers. Scalar input `s` is interpreted as `s+sj` for start, and scalar `a` as `a+aj` for increment.
429
- * Division: Complex division (`/`). Perfect divisibility: Real part is `math.isclose(real_part % integer_divisor, 0)`.
430
- * ASK unit: `1+1j`. Primality: `abs(int(number.real))`.
431
- 4. Floating-Point Numbers (Treated as Float):
432
- * Start/Increment: Decimal numbers.
433
- * Division: Float division (`/`). Perfect divisibility: `math.isclose(number % integer_divisor, 0)`.
434
- * ASK unit: `1.0`. Primality: `abs(int(number))`.
435
- 5. Rational Numbers (`fractions.Fraction`):
436
- * Start/Increment: Fractions. Scalar input `s` is interpreted as `Fraction(s,1)`.
437
- * Division: Fraction division (`/`). Perfect divisibility: `(fraction / integer_divisor).denominator == 1`.
438
- * ASK unit: `Fraction(1,1)`. Primality: `abs(int(fraction))`.
439
- 6. Quaternions (`numpy.quaternion`):
440
- * 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.
441
- * Division: Quaternion division (`/`). Perfect divisibility: Scalar (w) part is `math.isclose(w_part % integer_divisor, 0)`.
442
- * ASK unit: `quaternion(1,1,1,1)`. Primality: `abs(int(number.w))`.
443
- """
444
-
445
- # Constants for Kececi Types (makes it easier to use from outside)
446
- TYPE_POSITIVE_REAL = 1
447
- TYPE_NEGATIVE_REAL = 2
448
- TYPE_COMPLEX = 3
449
- TYPE_FLOAT = 4
450
- TYPE_RATIONAL = 5
451
- TYPE_QUATERNION = 6
452
-
453
- # You can remove the __main__ block if this is purely a module
454
- # or keep it for testing the module directly.
455
- if __name__ == "__main__":
456
- print("Keçeci Numbers Module Loaded.")
457
- print("This module provides functions to generate and plot Keçeci Numbers.")
458
- print("Example: Use 'import kececinumbers as kn' in your script/notebook.")
459
- print("\nAvailable functions:")
460
- print("- kn.get_interactive()")
461
- print("- kn.get_with_params(kececi_type, iterations, ...)")
462
- print("- kn.get_random_type(iterations, ...)")
463
- print("- kn.plot_numbers(sequence, title)")
464
- print("- kn.unified_generator(...) (low-level)")
465
- print("\nAccess definitions with: kn.DEFINITIONS")
466
- print("\nAccess type constants like: kn.TYPE_COMPLEX")
467
-
468
- # Basic test
469
- print("\nRunning a quick test for Complex numbers (fixed params):")
470
- test_seq = get_with_params(TYPE_COMPLEX, 10, start_value_raw="1", add_value_base_scalar=2.0, fixed_params=True)
471
- if test_seq:
472
- for i, val in enumerate(test_seq[:5]):
473
- if isinstance(val, complex): print(f" {i}: {val.real:.1f}{val.imag:+.1f}j")
474
- else: print(f" {i}: {val}")
475
- # plot_numbers(test_seq, "Module Test - Complex") # Uncomment to plot if running directly
1
+ # kececinumbers.py
2
+
3
+ import matplotlib.pyplot as plt
4
+ import random
5
+ import numpy as np
6
+ import math
7
+ from fractions import Fraction
8
+ import quaternion # pip install numpy numpy-quaternion
9
+
10
+
11
+ # --- Helper Functions ---
12
+ def is_prime(n_input):
13
+ """
14
+ Checks if a given number (or its relevant part for complex types)
15
+ is prime.
16
+ For Rational numbers, its integer part is used.
17
+ For Complex/Quaternion numbers, its real/scalar part is used.
18
+ """
19
+ value_to_check = 0
20
+ if isinstance(n_input, (int, float)):
21
+ value_to_check = abs(int(n_input))
22
+ elif isinstance(n_input, Fraction):
23
+ value_to_check = abs(int(n_input)) # Integer part of the Fraction
24
+ elif isinstance(n_input, complex):
25
+ value_to_check = abs(int(n_input.real))
26
+ elif isinstance(n_input, np.quaternion): # numpy-quaternion type check
27
+ value_to_check = abs(int(n_input.w)) # Scalar (real) part
28
+ else: # Default for other cases
29
+ try:
30
+ value_to_check = abs(int(n_input))
31
+ except (ValueError, TypeError):
32
+ return False # Consider not prime
33
+
34
+ if value_to_check < 2:
35
+ return False
36
+ for i in range(2, int(value_to_check**0.5) + 1):
37
+ if value_to_check % i == 0:
38
+ return False
39
+ return True
40
+
41
+ # --- Main Keçeci Number Generator ---
42
+ def unified_generator(kececi_type, start_input_raw, add_input_base_scalar, iterations):
43
+ """
44
+ Calculates Unified Keçeci Numbers.
45
+ kececi_type: 1 (Positive Real), 2 (Negative Real), 3 (Complex), 4 (Floating-Point),
46
+ 5 (Rational), 6 (Quaternion)
47
+ start_input_raw: Starting value (as string or number, appropriate for the type)
48
+ add_input_base_scalar: Base scalar value for increment (interpreted based on type)
49
+ iterations: Number of iterations
50
+ """
51
+ sequence = []
52
+ current_value = None
53
+ _add_value_typed = None # Type-specific add value
54
+ ask_unit = None
55
+ use_integer_division = False # Generally False, True only for int-based types
56
+
57
+ # Set initial value, add value, and ASK unit based on type
58
+ if kececi_type == 1: # Positive Real (treated as integer)
59
+ current_value = int(start_input_raw)
60
+ _add_value_typed = int(add_input_base_scalar)
61
+ ask_unit = 1
62
+ use_integer_division = True
63
+ elif kececi_type == 2: # Negative Real (treated as integer)
64
+ current_value = int(start_input_raw)
65
+ _add_value_typed = int(add_input_base_scalar) # Usually negative
66
+ ask_unit = 1
67
+ use_integer_division = True
68
+ elif kececi_type == 3: # Complex Numbers
69
+ # Adım 1: start_input_raw'ı bir complex sayıya dönüştür
70
+ if isinstance(start_input_raw, complex):
71
+ # Zaten bir complex sayı ise, doğrudan kullan
72
+ start_complex_val = start_input_raw
73
+ elif isinstance(start_input_raw, (int, float)):
74
+ # Eğer int veya float ise, s+sj yap (kullanıcının beklentisi buysa)
75
+ s_scalar = float(start_input_raw)
76
+ start_complex_val = complex(s_scalar, s_scalar)
77
+ else: # String ise (örn: "1+2j", "3", "3.0")
78
+ try:
79
+ # Doğrudan complex sayı string'i mi diye dene (örn: "1+2j")
80
+ start_complex_val = complex(str(start_input_raw))
81
+ except ValueError:
82
+ # Eğer complex() başarısız olduysa, skaler bir sayı string'i olabilir (örn: "3")
83
+ # Bu durumda s+sj yapalım
84
+ try:
85
+ s_scalar_from_string = float(str(start_input_raw))
86
+ start_complex_val = complex(s_scalar_from_string, s_scalar_from_string)
87
+ except ValueError:
88
+ raise ValueError(f"Cannot convert start_input_raw '{start_input_raw}' to a complex number.")
89
+
90
+ current_value = start_complex_val
91
+
92
+ # Adım 2: add_input_base_scalar'dan _add_value_typed oluştur
93
+ a_scalar_for_add = float(add_input_base_scalar)
94
+ _add_value_typed = complex(a_scalar_for_add, a_scalar_for_add) # a+aj
95
+
96
+ ask_unit = 1 + 1j
97
+ # use_integer_division = False (Zaten varsayılan)
98
+ elif kececi_type == 4: # Floating-Point Numbers
99
+ current_value = float(start_input_raw)
100
+ _add_value_typed = float(add_input_base_scalar)
101
+ ask_unit = 1.0
102
+ elif kececi_type == 5: # Rational Numbers
103
+ if isinstance(start_input_raw, Fraction):
104
+ current_value = start_input_raw
105
+ else: # String ("3/2", "5") or number (5 -> 5/1)
106
+ current_value = Fraction(str(start_input_raw))
107
+ _add_value_typed = Fraction(str(add_input_base_scalar)) # Convert scalar base to Fraction
108
+ ask_unit = Fraction(1, 1)
109
+ elif kececi_type == 6: # Quaternions
110
+ s_val_q_raw = float(start_input_raw) if not isinstance(start_input_raw, np.quaternion) else start_input_raw
111
+ a_val_q_scalar = float(add_input_base_scalar)
112
+ if isinstance(s_val_q_raw, np.quaternion):
113
+ current_value = s_val_q_raw
114
+ else: # From scalar, make q(s,s,s,s)
115
+ current_value = np.quaternion(s_val_q_raw, s_val_q_raw, s_val_q_raw, s_val_q_raw)
116
+ _add_value_typed = np.quaternion(a_val_q_scalar, a_val_q_scalar, a_val_q_scalar, a_val_q_scalar)
117
+ ask_unit = np.quaternion(1, 1, 1, 1) # Like (1+1i+1j+1k)
118
+ else:
119
+ raise ValueError("Invalid Keçeci Number Type")
120
+
121
+ sequence.append(current_value)
122
+ last_divisor_used = None # Last divisor used (2 or 3)
123
+ ask_counter = 0 # 0: first time +unit, 1: second time -unit
124
+
125
+ for _ in range(iterations):
126
+ added_value = current_value + _add_value_typed
127
+ sequence.append(added_value)
128
+
129
+ value_for_primality_check = added_value
130
+
131
+ primary_divisor = 3 if last_divisor_used is None or last_divisor_used == 2 else 2
132
+ alternative_divisor = 2 if primary_divisor == 3 else 3
133
+
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)
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)
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
+ can_divide = math.isclose(added_value.w % divisor_candidate, 0)
152
+
153
+ if can_divide:
154
+ if use_integer_division:
155
+ result_value = added_value // divisor_candidate
156
+ else:
157
+ result_value = added_value / divisor_candidate
158
+ last_divisor_used = divisor_candidate
159
+ divided_successfully = True
160
+ break
161
+
162
+ if not divided_successfully:
163
+ if is_prime(value_for_primality_check):
164
+ modified_value = None
165
+ if ask_counter == 0:
166
+ modified_value = added_value + ask_unit
167
+ ask_counter = 1
168
+ else:
169
+ modified_value = added_value - ask_unit
170
+ ask_counter = 0
171
+ sequence.append(modified_value)
172
+
173
+ current_target_for_division_mod = modified_value
174
+ divided_after_modification = False
175
+ for divisor_candidate_mod in [primary_divisor, alternative_divisor]:
176
+ can_divide_mod = False
177
+ if kececi_type in [1, 2]:
178
+ can_divide_mod = (current_target_for_division_mod % divisor_candidate_mod == 0)
179
+ elif kececi_type == 3:
180
+ can_divide_mod = math.isclose(current_target_for_division_mod.real % divisor_candidate_mod, 0)
181
+ elif kececi_type == 4:
182
+ can_divide_mod = math.isclose(current_target_for_division_mod % divisor_candidate_mod, 0) or \
183
+ math.isclose(current_target_for_division_mod % divisor_candidate_mod, divisor_candidate_mod)
184
+ elif kececi_type == 5:
185
+ if divisor_candidate_mod != 0:
186
+ quotient_rational_mod = current_target_for_division_mod / divisor_candidate_mod
187
+ can_divide_mod = (quotient_rational_mod.denominator == 1)
188
+ elif kececi_type == 6:
189
+ can_divide_mod = math.isclose(current_target_for_division_mod.w % divisor_candidate_mod, 0)
190
+
191
+ if can_divide_mod:
192
+ if use_integer_division:
193
+ result_value = current_target_for_division_mod // divisor_candidate_mod
194
+ else:
195
+ result_value = current_target_for_division_mod / divisor_candidate_mod
196
+ last_divisor_used = divisor_candidate_mod
197
+ divided_after_modification = True
198
+ break
199
+ if not divided_after_modification:
200
+ result_value = modified_value
201
+ else:
202
+ result_value = added_value
203
+
204
+ sequence.append(result_value)
205
+ current_value = result_value
206
+
207
+ return sequence
208
+
209
+ # --- Control Mechanisms (Exportable Functions) ---
210
+ def get_interactive():
211
+ """
212
+ Interactively gets parameters from the user and generates Keçeci Numbers.
213
+ """
214
+ print("Keçeci Number Types:")
215
+ print("1: Positive Real Numbers (Integer: e.g., 1)")
216
+ print("2: Negative Real Numbers (Integer: e.g., -3)")
217
+ print("3: Complex Numbers (e.g., 3+4j)")
218
+ print("4: Floating-Point Numbers (e.g., 2.5)")
219
+ print("5: Rational Numbers (e.g., 3/2, 5)")
220
+ print("6: Quaternions (scalar start input becomes q(s,s,s,s): e.g., 1 or 2.5)")
221
+
222
+ while True:
223
+ try:
224
+ type_choice = int(input("Please select Keçeci Number Type (1-6): "))
225
+ if 1 <= type_choice <= 6: break
226
+ else: print("Invalid type.")
227
+ except ValueError: print("Please enter a numeric value.")
228
+
229
+ start_prompt = "Enter the starting number (e.g., 0 or 2.5, complex:3+4j, rational: 3/4, quaternions: 1) : "
230
+ if type_choice == 3: start_prompt += "(e.g., 3+4j or just 3): "
231
+ elif type_choice == 5: start_prompt += "(e.g., 7/2 or 5): "
232
+
233
+ start_input_val_raw = input(start_prompt)
234
+
235
+ while True:
236
+ try:
237
+ add_base_scalar_val = float(input("Enter the base scalar value for increment (e.g., 9): "))
238
+ break
239
+ except ValueError: print("Please enter a numeric value.")
240
+
241
+ while True:
242
+ try:
243
+ num_iterations = int(input("Enter the number of iterations (positive integer: e.g., 30): "))
244
+ if num_iterations > 0: break
245
+ else: print("Number of iterations must be positive.")
246
+ except ValueError: print("Please enter an integer value.")
247
+
248
+ return unified_generator(type_choice, start_input_val_raw, add_base_scalar_val, num_iterations)
249
+
250
+ 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):
251
+ """
252
+ Generates Keçeci Numbers with specified or randomized parameters.
253
+ If fixed_params is False, start_value_raw and add_value_base_scalar are used as bases for randomization.
254
+ random_range_factor influences the range of random values.
255
+ """
256
+ actual_start_raw = start_value_raw
257
+ actual_add_base = add_value_base_scalar
258
+
259
+ if not fixed_params:
260
+ # Basic randomization logic, can be expanded
261
+ if kececi_type_choice == 1: # Positive
262
+ actual_start_raw = str(random.randint(0, int(random_range_factor)))
263
+ actual_add_base = float(random.randint(1, int(random_range_factor*1.5)))
264
+ elif kececi_type_choice == 2: # Negative
265
+ actual_start_raw = str(random.randint(-int(random_range_factor), 0))
266
+ actual_add_base = float(random.randint(-int(random_range_factor*1.5), -1))
267
+ elif kececi_type_choice == 3: # Complex
268
+ actual_start_raw = str(random.uniform(-random_range_factor/2, random_range_factor/2))
269
+ actual_add_base = random.uniform(1, random_range_factor/2)
270
+ elif kececi_type_choice == 4: # Float
271
+ actual_start_raw = str(random.uniform(-random_range_factor, random_range_factor))
272
+ actual_add_base = random.uniform(0.1, random_range_factor/2)
273
+ elif kececi_type_choice == 5: # Rational
274
+ num = random.randint(-random_range_factor, random_range_factor)
275
+ den = random.randint(1, int(random_range_factor/2) if random_range_factor/2 >=1 else 1)
276
+ actual_start_raw = f"{num}/{den}"
277
+ actual_add_base = float(random.randint(1,random_range_factor))
278
+ elif kececi_type_choice == 6: # Quaternion
279
+ actual_start_raw = str(random.uniform(-random_range_factor/2, random_range_factor/2))
280
+ actual_add_base = random.uniform(1, random_range_factor/2)
281
+ else: # Fixed parameters, but adjust for negative type if default add is positive
282
+ if kececi_type_choice == 2 and actual_add_base > 0:
283
+ actual_add_base = -actual_add_base
284
+
285
+
286
+ return unified_generator(kececi_type_choice, actual_start_raw, actual_add_base, iterations)
287
+
288
+
289
+ def get_random_type(num_iterations, use_fixed_params_for_selected_type=True,
290
+ fixed_start_raw="0", fixed_add_base_scalar=9.0, random_factor=10):
291
+ """
292
+ Generates Keçeci Numbers for a randomly selected type.
293
+ """
294
+ random_type_choice = random.randint(1, 6)
295
+ type_names_list = ["Positive Integer", "Negative Integer", "Complex", "Float", "Rational", "Quaternion"]
296
+ print(f"Randomly selected Keçeci Number Type: {random_type_choice} ({type_names_list[random_type_choice-1]})")
297
+
298
+ return get_with_params(random_type_choice, num_iterations,
299
+ start_value_raw=fixed_start_raw,
300
+ add_value_base_scalar=fixed_add_base_scalar,
301
+ fixed_params=use_fixed_params_for_selected_type,
302
+ random_range_factor=random_factor)
303
+
304
+ # --- Plotting Function (can be called from the notebook) ---
305
+ def plot_numbers(sequence, title="Keçeci Numbers"):
306
+ """
307
+ Plots the generated Keçeci Number sequence.
308
+ """
309
+ plt.figure(figsize=(14, 8))
310
+
311
+ if not sequence:
312
+ print("Sequence is empty, nothing to plot.")
313
+ return
314
+
315
+ first_elem = sequence[0]
316
+
317
+ if isinstance(first_elem, np.quaternion):
318
+ w_parts = [q.w for q in sequence]
319
+ # x_parts = [q.x for q in sequence] # Optional
320
+ # y_parts = [q.y for q in sequence] # Optional
321
+ # z_parts = [q.z for q in sequence] # Optional
322
+ vector_norms = [np.sqrt(q.x**2 + q.y**2 + q.z**2) for q in sequence]
323
+
324
+ plt.subplot(2, 1, 1)
325
+ plt.plot(w_parts, marker='o', linestyle='-', label='w (Scalar Part)')
326
+ plt.title(title + " - Quaternion Scalar Part (w)")
327
+ plt.xlabel("Index"); plt.ylabel("Value"); plt.grid(True); plt.legend()
328
+
329
+ plt.subplot(2, 1, 2)
330
+ plt.plot(vector_norms, marker='x', linestyle='--', color='purple', label='Vector Part Norm (|xi+yj+zk|)')
331
+ plt.title(title + " - Quaternion Vector Part Norm")
332
+ plt.xlabel("Index"); plt.ylabel("Value"); plt.grid(True); plt.legend()
333
+
334
+ elif isinstance(first_elem, complex):
335
+ real_parts = [n.real for n in sequence]
336
+ imag_parts = [n.imag for n in sequence]
337
+
338
+ plt.subplot(2, 1, 1)
339
+ plt.plot(real_parts, marker='o', linestyle='-', label='Real Part')
340
+ plt.title(title + " - Complex Real Part"); plt.xlabel("Index")
341
+ plt.ylabel("Value"); plt.grid(True); plt.legend()
342
+
343
+ plt.subplot(2, 1, 2)
344
+ plt.plot(imag_parts, marker='x', linestyle='--', color='red', label='Imaginary Part')
345
+ plt.title(title + " - Complex Imaginary Part"); plt.xlabel("Index")
346
+ plt.ylabel("Value"); plt.grid(True); plt.legend()
347
+
348
+ plt.figure(figsize=(8,8))
349
+ plt.plot(real_parts, imag_parts, marker='.', linestyle='-')
350
+ if real_parts:
351
+ plt.plot(real_parts[0], imag_parts[0], 'go', markersize=8, label='Start')
352
+ plt.plot(real_parts[-1], imag_parts[-1], 'ro', markersize=8, label='End')
353
+ plt.title(title + " - Trajectory in Complex Plane"); plt.xlabel("Real Axis")
354
+ plt.ylabel("Imaginary Axis"); plt.axhline(0, color='black', lw=0.5)
355
+ plt.axvline(0, color='black', lw=0.5); plt.grid(True); plt.legend(); plt.axis('equal')
356
+
357
+ elif isinstance(first_elem, Fraction):
358
+ float_sequence = [float(f) for f in sequence]
359
+ plt.plot(float_sequence, marker='o', linestyle='-')
360
+ plt.title(title + " (Rational Numbers - plotted as float)")
361
+ plt.xlabel("Index"); plt.ylabel("Value (float)"); plt.grid(True)
362
+
363
+ else:
364
+ try:
365
+ numeric_sequence = np.array(sequence, dtype=float)
366
+ plt.plot(numeric_sequence, marker='o', linestyle='-')
367
+ except ValueError:
368
+ print("Sequence contains non-plottable values. Plotting only numeric ones.")
369
+ plt.plot([x for x in sequence if isinstance(x, (int, float))], marker='o', linestyle='-')
370
+ plt.title(title); plt.xlabel("Index")
371
+ plt.ylabel("Value"); plt.grid(True)
372
+
373
+ plt.tight_layout()
374
+ plt.show()
375
+
376
+ # --- DEFINITIONS (as a multiline string, can be accessed as kececinumbers.DEFINITIONS) ---
377
+ DEFINITIONS = """
378
+ Keçeci NumberS UNIFIED DEFINITION
379
+
380
+ A Keçeci Number sequence is derived from a `start_input_raw` and an `add_input_base_scalar`.
381
+ These inputs are interpreted according to the selected Keçeci Number Type to become the `current_value` and a type-specific `_add_value_typed`.
382
+
383
+ In each step:
384
+ 1. `added_value = current_value + _add_value_typed`.
385
+ 2. `added_value` is recorded in the sequence.
386
+ 3. A `result_value` is obtained by applying the Division Rule and, if necessary, the ASK Rule to `added_value`.
387
+ 4. `result_value` is recorded in the sequence.
388
+ 5. `current_value = result_value`.
389
+ This process is repeated for the specified `number_of_iterations`.
390
+
391
+ Division Rule:
392
+ * A `last_divisor_used` (2 or 3) is tracked.
393
+ * `primary_divisor`: If `last_divisor_used` is nonexistent (first step) or 2, it's 3; if it's 3, it's 2.
394
+ * `alternative_divisor`: The other of `primary_divisor` (2 or 3).
395
+ * `value_to_check_division`: This is `added_value` (or `modified_value` after ASK).
396
+ * 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).
397
+ * First, division by `primary_divisor` is attempted:
398
+ * 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),
399
+ then `result_value = value_to_check_division / primary_divisor`. `last_divisor_used = primary_divisor`.
400
+ * If unsuccessful, division by `alternative_divisor` is attempted:
401
+ * If `value_to_check_division` (or its relevant part) is "perfectly divisible" by `alternative_divisor`,
402
+ then `result_value = value_to_check_division / alternative_divisor`. `last_divisor_used = alternative_divisor`.
403
+
404
+ Primality and ASK (Augment/Shrink then Check) Rule (if Division Fails):
405
+ * `value_for_primality_check`: The part/representation of `added_value` used for primality testing (e.g., integer part, real part).
406
+ * If `is_prime(value_for_primality_check)` is true:
407
+ * An `ask_counter` (0 or 1) is used.
408
+ * `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).
409
+ * If `ask_counter` is 0: `modified_value = added_value + ask_unit`, `ask_counter = 1`.
410
+ * If `ask_counter` is 1: `modified_value = added_value - ask_unit`, `ask_counter = 0`.
411
+ * `modified_value` is added to the sequence.
412
+ * The Division Rule above is re-attempted on this `modified_value`.
413
+ * If division is successful, `result_value` is the quotient.
414
+ * If unsuccessful, `result_value = modified_value`.
415
+ * If `is_prime(value_for_primality_check)` is false (and it wasn't divisible):
416
+ * `result_value = added_value`.
417
+
418
+ Number Types and Specifics:
419
+ 1. Positive Real Numbers (Treated as Integer):
420
+ * Start/Increment: Positive integers.
421
+ * Division: Integer division (`//`). Perfect divisibility: `% == 0`.
422
+ * ASK unit: `1`. Primality: `abs(int(number))`.
423
+ 2. Negative Real Numbers (Treated as Integer):
424
+ * Start/Increment: Generally negative integers.
425
+ * Division: Integer division (`//`). Perfect divisibility: `% == 0`.
426
+ * ASK unit: `1`. Primality: `abs(int(number))`.
427
+ 3. Complex Numbers (`complex`):
428
+ * Start/Increment: Complex numbers. Scalar input `s` is interpreted as `s+sj` for start, and scalar `a` as `a+aj` for increment.
429
+ * Division: Complex division (`/`). Perfect divisibility: Real part is `math.isclose(real_part % integer_divisor, 0)`.
430
+ * ASK unit: `1+1j`. Primality: `abs(int(number.real))`.
431
+ 4. Floating-Point Numbers (Treated as Float):
432
+ * Start/Increment: Decimal numbers.
433
+ * Division: Float division (`/`). Perfect divisibility: `math.isclose(number % integer_divisor, 0)`.
434
+ * ASK unit: `1.0`. Primality: `abs(int(number))`.
435
+ 5. Rational Numbers (`fractions.Fraction`):
436
+ * Start/Increment: Fractions. Scalar input `s` is interpreted as `Fraction(s,1)`.
437
+ * Division: Fraction division (`/`). Perfect divisibility: `(fraction / integer_divisor).denominator == 1`.
438
+ * ASK unit: `Fraction(1,1)`. Primality: `abs(int(fraction))`.
439
+ 6. Quaternions (`numpy.quaternion`):
440
+ * 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.
441
+ * Division: Quaternion division (`/`). Perfect divisibility: Scalar (w) part is `math.isclose(w_part % integer_divisor, 0)`.
442
+ * ASK unit: `quaternion(1,1,1,1)`. Primality: `abs(int(number.w))`.
443
+ """
444
+
445
+ # Constants for Kececi Types (makes it easier to use from outside)
446
+ TYPE_POSITIVE_REAL = 1
447
+ TYPE_NEGATIVE_REAL = 2
448
+ TYPE_COMPLEX = 3
449
+ TYPE_FLOAT = 4
450
+ TYPE_RATIONAL = 5
451
+ TYPE_QUATERNION = 6
452
+
453
+ # You can remove the __main__ block if this is purely a module
454
+ # or keep it for testing the module directly.
455
+ if __name__ == "__main__":
456
+ print("Keçeci Numbers Module Loaded.")
457
+ print("This module provides functions to generate and plot Keçeci Numbers.")
458
+ print("Example: Use 'import kececinumbers as kn' in your script/notebook.")
459
+ print("\nAvailable functions:")
460
+ print("- kn.get_interactive()")
461
+ print("- kn.get_with_params(kececi_type, iterations, ...)")
462
+ print("- kn.get_random_type(iterations, ...)")
463
+ print("- kn.plot_numbers(sequence, title)")
464
+ print("- kn.unified_generator(...) (low-level)")
465
+ print("\nAccess definitions with: kn.DEFINITIONS")
466
+ print("\nAccess type constants like: kn.TYPE_COMPLEX")
467
+
468
+ # Basic test
469
+ print("\nRunning a quick test for Complex numbers (fixed params):")
470
+ test_seq = get_with_params(TYPE_COMPLEX, 10, start_value_raw="1", add_value_base_scalar=2.0, fixed_params=True)
471
+ if test_seq:
472
+ for i, val in enumerate(test_seq[:5]):
473
+ if isinstance(val, complex): print(f" {i}: {val.real:.1f}{val.imag:+.1f}j")
474
+ else: print(f" {i}: {val}")
475
+ # plot_numbers(test_seq, "Module Test - Complex") # Uncomment to plot if running directly