commit state before changing what noether considers primitive roots
This commit is contained in:
parent
a168a728ce
commit
33bcffdc69
11 changed files with 441 additions and 36 deletions
19
README
Normal file
19
README
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Noether is a project I plan to complete over a LONG period of time (years).
|
||||
A collection of useful commands for solving various math problems.
|
||||
|
||||
A personal little MATLAB alternative I suppose :)
|
||||
|
||||
|
||||
In future I'd love to start using a developed TUI library like [textual](https://github.com/Textualize/textual?tab=readme-ov-file),
|
||||
but for now everything is just a custom ANSI wrapper thing.
|
||||
|
||||
|
||||
|
||||
NOTES:
|
||||
1. could I define something like "primality classes"?
|
||||
ie class one: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, ...
|
||||
class two: 3, 5, 11, 17, 31, ... (class 1 with a prime index, ie 11 is the 5th class 1 prime)
|
||||
class three: 5, 11, 31, ... (class 2 with a prime index, ie 11 is the 3rd class 2 prime)
|
||||
|
||||
2. figure out all possible patterns that can appear amongst primitive roots
|
||||
can we then categorise primes by their primitive root behaviour?
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# Euler's Totient (Phi) Function
|
||||
def totient(n):
|
||||
phi = int(n > 1 and n)
|
||||
for p in range(2, int(n ** .5) + 1):
|
||||
if not n % p:
|
||||
phi -= phi // p
|
||||
while not n % p:
|
||||
n //= p
|
||||
#if n is > 1 it means it is prime
|
||||
if n > 1: phi -= phi // n
|
||||
return phi
|
||||
|
||||
|
||||
def orbit(b, m):
|
||||
generated = []
|
||||
x = b
|
||||
for i in range(m):
|
||||
x = (x * b) % m
|
||||
if x not in generated:
|
||||
generated.append(x)
|
||||
return generated
|
||||
|
||||
def order(b, m):
|
||||
return len(orbit(b, m))
|
||||
8
py/NOTES
Normal file
8
py/NOTES
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
TODO:
|
||||
NOTE: currently my "primitive roots" are actually just the numbers that generate every integer below the modulus, that is `a` such that `orbit(a) = Z_n`
|
||||
1. How I'm calculating something as a primtive root, and its order is not correct.
|
||||
For instance, consider a modulus n=4, a=3 is a primitive root, and a=2 SHOULD have order 0 (it is aperiodic)
|
||||
|
||||
|
||||
This site is useful as a reference:
|
||||
https://owlsmath.neocities.org/Primitive%20Root%20Calculator/calculator
|
||||
76
py/mquiet.py
Normal file
76
py/mquiet.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Modulo Test
|
||||
from prbraid.math import *
|
||||
from prbraid.color import *
|
||||
|
||||
import sys
|
||||
|
||||
PROMPT = '[n]: '
|
||||
|
||||
'''
|
||||
Pairwise orbit cumulative summation (cum orb)
|
||||
'''
|
||||
def orb_cum(cum, orb):
|
||||
for i in range(len(cum)):
|
||||
cum[i] += orb[i]
|
||||
|
||||
def main():
|
||||
while True:
|
||||
n = None
|
||||
strlen_n = None
|
||||
try:
|
||||
uprint(PROMPT, color=Color.Blue, end='')
|
||||
n = input()
|
||||
strlen_n = len(n)
|
||||
n = int(n)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# calculate phi of n
|
||||
phi = totient(n)
|
||||
# determine if n is prime
|
||||
prime = n - 1 == phi
|
||||
if prime:
|
||||
uprint('', moveup=1, end='', flush=False)
|
||||
column = len(PROMPT) + strlen_n + 1
|
||||
sys.stdout.write(f'\033[{column}C')
|
||||
uprint('[PRIME]', color=Color.Magenta, style=Color.Bold, flush=True)
|
||||
# primitive root values
|
||||
proot_v = []
|
||||
# primitive root count
|
||||
proot_c = 0
|
||||
# cumulative sum of primitive root orbits
|
||||
prorb_cum = []
|
||||
|
||||
# find all invertible elements (skipped for now)
|
||||
for a in range(n):
|
||||
orb, ord = orbit(a, n)
|
||||
# check if `a` is a primitive root
|
||||
proot = (ord + 1 == n)
|
||||
proot_c += proot
|
||||
|
||||
if proot:
|
||||
proot_v.append(a)
|
||||
if not prorb_cum:
|
||||
prorb_cum = orb
|
||||
else:
|
||||
orb_cum(prorb_cum, orb)
|
||||
print(a)
|
||||
uprint('Cum Orb: ', end='', color=Color.Cyan)
|
||||
uprint(f'{prorb_cum}', flush=True)
|
||||
prorb_cum_mod = [x % n for x in prorb_cum]
|
||||
uprint(f' {prorb_cum_mod}', flush=True)
|
||||
|
||||
uprint('Roots: ', end='', color=Color.Cyan)
|
||||
uprint(proot_v, flush=True)
|
||||
root_delta = [proot_v[i+1] - proot_v[i] for i in range(proot_c - 1)]
|
||||
uprint('Delta: ', end='', color=Color.Cyan)
|
||||
uprint(root_delta, flush=True)
|
||||
|
||||
uprint('Roots/Phi: ', end='', color=Color.Cyan)
|
||||
uprint(f'{proot_c}/{phi}\n', flush=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
pass
|
||||
|
|
@ -1,11 +1,20 @@
|
|||
# Modulo Test
|
||||
from prbraid.math import *
|
||||
from prbraid.color import *
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import readline
|
||||
|
||||
from noether.math import *
|
||||
from noether.cli import *
|
||||
|
||||
|
||||
PROMPT = '[n]: '
|
||||
|
||||
'''
|
||||
Pairwise orbit cumulative summation (cum orb)
|
||||
'''
|
||||
def orb_cum(cum, orb):
|
||||
for i in range(len(cum)):
|
||||
cum[i] += orb[i]
|
||||
|
||||
def main():
|
||||
while True:
|
||||
n = None
|
||||
|
|
@ -22,13 +31,17 @@ def main():
|
|||
phi = totient(n)
|
||||
# determine if n is prime
|
||||
prime = n - 1 == phi
|
||||
# sys.stdout.write('\x1b[1A')
|
||||
# sys.stdout.flush()
|
||||
if prime:
|
||||
uprint('', moveup=1, end='', flush=False)
|
||||
column = len(PROMPT) + strlen_n + 1
|
||||
sys.stdout.write(f'\033[{column}C')
|
||||
uprint('[PRIME]', color=Color.Magenta, style=Color.Bold, flush=True)
|
||||
# primitive root values
|
||||
proot_v = []
|
||||
# primitive root count
|
||||
proot_c = 0
|
||||
# cumulative sum of primitive root orbits
|
||||
prorb_cum = []
|
||||
|
||||
# calculate left padding to align i values
|
||||
# lpadded = strlen_n
|
||||
|
|
@ -37,11 +50,11 @@ def main():
|
|||
|
||||
# find all invertible elements (skipped for now)
|
||||
for a in range(n):
|
||||
orb = orbit(a, n)
|
||||
ord = len(orb)
|
||||
orb, ord = orbit(a, n)
|
||||
|
||||
# check if `a` is a primitive root
|
||||
proot = (ord + 1 == n)
|
||||
proot_c += proot
|
||||
|
||||
# calculate padding
|
||||
lpad = ' ' * (strlen_n - len(str(a)))
|
||||
|
|
@ -51,20 +64,39 @@ def main():
|
|||
color_ord = None
|
||||
color_orb = None
|
||||
style_ord = None
|
||||
style_orb = None
|
||||
style_orb = None
|
||||
if proot:
|
||||
color_a = Color.Green
|
||||
color_ord = Color.Yellow
|
||||
color_orb = Color.Green
|
||||
style_ord = Color.Bold
|
||||
style_orb = Color.Bold
|
||||
proot_v.append(a)
|
||||
if not prorb_cum:
|
||||
prorb_cum = orb
|
||||
else:
|
||||
orb_cum(prorb_cum, orb)
|
||||
elif gcd(a, n) == 1:
|
||||
color_a = Color.Yellow
|
||||
|
||||
uprint(f'{lpad}{a}', color=color_a, style=Color.Bold, end=' ', flush=False)
|
||||
uprint(f'->', end=' ', flush=False)
|
||||
uprint('->', end=' ', flush=False)
|
||||
uprint(f'{ord}{rpad}', color=color_ord, style=style_ord, end=' ', flush=False)
|
||||
uprint(f'|', end=' ', flush=False)
|
||||
uprint('|', end=' ', flush=False)
|
||||
uprint(f'{orb}', color=color_orb, style=style_orb, flush=True)
|
||||
print() # empty new line
|
||||
uprint('Cum Orb: ', end='', color=Color.Cyan)
|
||||
uprint(f'{prorb_cum}', flush=True)
|
||||
prorb_cum_mod = [x % n for x in prorb_cum]
|
||||
uprint(f' {prorb_cum_mod}', flush=True)
|
||||
|
||||
uprint('Roots: ', end='', color=Color.Cyan)
|
||||
uprint(proot_v, flush=True)
|
||||
root_delta = [proot_v[i+1] - proot_v[i] for i in range(proot_c - 1)]
|
||||
uprint('Delta: ', end='', color=Color.Cyan)
|
||||
uprint(root_delta, flush=True)
|
||||
|
||||
uprint('Roots/Phi: ', end='', color=Color.Cyan)
|
||||
uprint(f'{proot_c}/{phi}\n', flush=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
172
py/noether/ansi.py
Normal file
172
py/noether/ansi.py
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
'''
|
||||
=== ANSI Escape Sequences ===
|
||||
This file exists to organise and name
|
||||
all important ANSI escape sequences
|
||||
for management of the CLI.
|
||||
'''
|
||||
|
||||
from sys import stdout
|
||||
from enum import StrEnum
|
||||
|
||||
RESET = '\x1b[0m'
|
||||
|
||||
class Color(StrEnum):
|
||||
BLACK = '\x1b[30m'
|
||||
RED = '\x1b[31m'
|
||||
GREEN = '\x1b[32m'
|
||||
YELLOW = '\x1b[33m'
|
||||
BLUE = '\x1b[34m'
|
||||
MAGENTA = '\x1b[35m'
|
||||
CYAN = '\x1b[36m'
|
||||
WHITE = '\x1b[37m'
|
||||
|
||||
class Style(StrEnum):
|
||||
BOLD = '\x1b[1m'
|
||||
DIM = '\x1b[2m'
|
||||
ITALICS = '\x1b[3m'
|
||||
UNDERLINE = '\x1b[4m'
|
||||
BLINK = '\x1b[5m'
|
||||
REVERSE = '\x1b[7m'
|
||||
HIDE = '\x1b[8m'
|
||||
|
||||
_DISABLE = [
|
||||
'\x1b[21m', # BOLD
|
||||
'\x1b[22m', # DIM
|
||||
'\x1b[24m', # UNDERLINE
|
||||
'\x1b[25m', # BLINK
|
||||
'\x1b[27m', # REVERSE
|
||||
'\x1b[28m' # HIDE
|
||||
]
|
||||
|
||||
'''
|
||||
Implements cursor movement functionality.
|
||||
NOTE:
|
||||
The Cursor class currently has no ability
|
||||
to handle EXACT line (row) numbers. Currently
|
||||
I'm assuming all functionality of noether can
|
||||
be implemented solely via relative movements.
|
||||
'''
|
||||
class Cursor(StrEnum):
|
||||
# SAVE current / RESTORE last saved cursor position
|
||||
SAVE = f'\x1b[7'
|
||||
RESTORE = f'\x1b[8'
|
||||
|
||||
_MV_UP = 'A'
|
||||
_MV_DOWN = 'B'
|
||||
_MV_LEFT = 'C'
|
||||
_MV_RIGHT = 'D'
|
||||
# NEXT/PREV are the same as DOWN/UP (respectively)
|
||||
# except that the cursor will reset to column 0
|
||||
_MV_NEXT = 'E'
|
||||
_MV_PREV = 'F'
|
||||
# move cursor to the start of the current line
|
||||
_MV_START = '\r' # there is no ESC CSI for carriage return
|
||||
|
||||
_MV_COLUMN = 'G'
|
||||
|
||||
'''
|
||||
Generates an ESC code sequence corresponding
|
||||
to a horizontal movement relative to the
|
||||
cursor's current vertical position.
|
||||
+ive = right
|
||||
-ive = left
|
||||
'''
|
||||
@staticmethod
|
||||
def _XMOVE_REL(n: int) -> str:
|
||||
if n > 0:
|
||||
return f'\x1b[{n}{Cursor._MV_RIGHT}'
|
||||
if n < 0:
|
||||
return f'\x1b[{n}{Cursor._MV_LEFT}'
|
||||
return ''
|
||||
|
||||
'''
|
||||
Generates an ESC code sequence corresponding
|
||||
to a horizontal movement to an EXACT column.
|
||||
'''
|
||||
@staticmethod
|
||||
def _XMOVE(n: int) -> str:
|
||||
return f'\x1b[{n}{Cursor._MV_COLUMN}'
|
||||
|
||||
'''
|
||||
Generates an ESC code sequence corresponding
|
||||
to a vertical movement relative to the
|
||||
cursor's current vertical position.
|
||||
+ive = down
|
||||
-ive = up
|
||||
'''
|
||||
@staticmethod
|
||||
def _YMOVE_REL(n: int, reset: bool = False) -> str:
|
||||
if n > 0:
|
||||
if reset:
|
||||
return f'\x1b[{n}{Cursor._MV_NEXT}'
|
||||
return f'\x1b[{n}{Cursor._MV_DOWN}'
|
||||
if n < 0:
|
||||
if reset:
|
||||
return f'\x1b[{n}{Cursor._MV_PREV}'
|
||||
return f'\x1b[{n}{Cursor._MV_UP}'
|
||||
return ''
|
||||
|
||||
'''
|
||||
Sets the cursor column (horizontal) position to an exact value.
|
||||
NOTE: does NOT flush stdout buffer
|
||||
'''
|
||||
@staticmethod
|
||||
def set_x(n: int) -> None:
|
||||
stdout.write(Cursor.XMOVE(n))
|
||||
|
||||
'''
|
||||
Moves the cursor left/right n columns (relative).
|
||||
NOTE: does NOT flush stdout buffer
|
||||
'''
|
||||
@staticmethod
|
||||
def move_x(n: int) -> None:
|
||||
stdout.write(Cursor._XMOVE_REL(n))
|
||||
|
||||
'''
|
||||
Moves the cursor up/down n rows and resets the
|
||||
cursor to be at the start of the line
|
||||
NOTE: does NOT flush stdout buffer
|
||||
'''
|
||||
@staticmethod
|
||||
def move_y(n: int, reset: bool = True) -> None:
|
||||
stdout.write(Cursor._YMOVE_REL(n, reset=reset))
|
||||
|
||||
'''
|
||||
Saves the current cursor position.
|
||||
NOTE: does NOT flush stdout buffer
|
||||
'''
|
||||
@staticmethod
|
||||
def save() -> None:
|
||||
stdout.write(Cursor.SAVE)
|
||||
|
||||
'''
|
||||
Restores the cursor position to a saved position.
|
||||
NOTE: does NOT flush stdout buffer
|
||||
'''
|
||||
@staticmethod
|
||||
def restore() -> None:
|
||||
stdout.write(Cursor.RESTORE)
|
||||
|
||||
'''
|
||||
Handles erasing content displayed on the screen.
|
||||
NOTE that the cursor position is NOT updated
|
||||
via these sequences. The \r code should be given after.
|
||||
'''
|
||||
class Erase(StrEnum):
|
||||
# erase everything from the current cursor
|
||||
# position to the START/END of the screen
|
||||
ER_SCR_END = '\x1b[0J'
|
||||
ER_SCR_START = '\x1b[1J'
|
||||
# ER_SCR_ALL = '\x1b[2J'
|
||||
ER_SCR_ALL = '\x1b[3J' # erase screen and delete all saved cursors
|
||||
|
||||
# ER_SAVED = '\x1b[3J' # erase saved lines
|
||||
|
||||
# erase everything from the current cursor
|
||||
# position to the START/END of the current line
|
||||
ER_LINE_END = '\x1b[0K'
|
||||
ER_LINE_START = '\x1b[1K'
|
||||
ER_LINE_ALL = '\x1b[2K'
|
||||
|
||||
@staticmethod
|
||||
# TODO COME BACK HERE
|
||||
0
py/noether/lib/__init__.py
Normal file
0
py/noether/lib/__init__.py
Normal file
42
py/noether/math.py
Normal file
42
py/noether/math.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from math import gcd
|
||||
|
||||
# Euler's Totient (Phi) Function
|
||||
def totient(n):
|
||||
phi = int(n > 1 and n)
|
||||
for p in range(2, int(n ** .5) + 1):
|
||||
if not n % p:
|
||||
phi -= phi // p
|
||||
while not n % p:
|
||||
n //= p
|
||||
#if n is > 1 it means it is prime
|
||||
if n > 1: phi -= phi // n
|
||||
return phi
|
||||
|
||||
def is_prime(n):
|
||||
return n - 1 == totient(n)
|
||||
|
||||
def orbit(b, m):
|
||||
orb = []
|
||||
ord = 0
|
||||
x = 1 # start at identity
|
||||
for i in range(m):
|
||||
x = (x * b) % m
|
||||
if x in orb:
|
||||
break
|
||||
orb.append(x)
|
||||
ord += 1
|
||||
return orb, ord
|
||||
|
||||
def order(b, m):
|
||||
return len(orbit(b, m))
|
||||
|
||||
|
||||
'''
|
||||
Functions for Testing my Conjectures
|
||||
|
||||
The conjecture is specifically that, for all positive integers n
|
||||
where n + 1 is prime, then conj_phigcd(n) is also prime.
|
||||
'''
|
||||
def conj_phigcd(n):
|
||||
phi = totient(n)
|
||||
return phi / gcd(n, phi) - 1
|
||||
80
py/primes.py
Normal file
80
py/primes.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Modulo Test
|
||||
from prbraid.math import *
|
||||
from prbraid.color import *
|
||||
|
||||
import sys
|
||||
from math import gcd
|
||||
from time import sleep
|
||||
|
||||
PROMPT = '[n]: '
|
||||
|
||||
'''
|
||||
Modify the body of this function, it will be run
|
||||
against every prime number ascending.
|
||||
'''
|
||||
def test_function(p: int, phi: int):
|
||||
p_color = Color.Green
|
||||
result_color = Color.Yellow
|
||||
|
||||
result = phi / gcd(p, phi) - 1
|
||||
if result < 0 or not is_prime(result):
|
||||
p_color = Color.Red
|
||||
result_color = Color.Red
|
||||
uprint(p, color=p_color, style=Color.Bold, end=' -> ', flush=False)
|
||||
uprint(result, color=result_color, flush=True)
|
||||
sleep(0.1)
|
||||
|
||||
|
||||
def main():
|
||||
n = -1
|
||||
while True:
|
||||
n += 1
|
||||
|
||||
# calculate phi of n
|
||||
phi = totient(n)
|
||||
# determine if n is prime
|
||||
prime = n - 1 == phi
|
||||
if not prime:
|
||||
continue
|
||||
|
||||
# # primitive root values
|
||||
# proot_v = []
|
||||
# # primitive root count
|
||||
# proot_c = 0
|
||||
# # cumulative sum of primitive root orbits
|
||||
# prorb_cum = []
|
||||
|
||||
# # find all invertible elements (skipped for now)
|
||||
# for a in range(n):
|
||||
# orb, ord = orbit(a, n)
|
||||
# # check if `a` is a primitive root
|
||||
# proot = (ord + 1 == n)
|
||||
# proot_c += proot
|
||||
|
||||
# if proot:
|
||||
# proot_v.append(a)
|
||||
# if not prorb_cum:
|
||||
# prorb_cum = orb
|
||||
# else:
|
||||
# orb_cum(prorb_cum, orb)
|
||||
# print(a)
|
||||
# uprint('Cum Orb: ', end='', color=Color.Cyan)
|
||||
# uprint(f'{prorb_cum}', flush=True)
|
||||
# prorb_cum_mod = [x % n for x in prorb_cum]
|
||||
# uprint(f' {prorb_cum_mod}', flush=True)
|
||||
|
||||
# uprint('Roots: ', end='', color=Color.Cyan)
|
||||
# uprint(proot_v, flush=True)
|
||||
# root_delta = [proot_v[i+1] - proot_v[i] for i in range(proot_c - 1)]
|
||||
# uprint('Delta: ', end='', color=Color.Cyan)
|
||||
# uprint(root_delta, flush=True)
|
||||
|
||||
# uprint('Roots/Phi: ', end='', color=Color.Cyan)
|
||||
# uprint(f'{proot_c}/{phi}\n', flush=True)
|
||||
test_function(n, phi)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
pass
|
||||
Loading…
Add table
Add a link
Reference in a new issue