begin shift to nim code base
This commit is contained in:
parent
33bcffdc69
commit
4b20f9961b
25 changed files with 625 additions and 303 deletions
8
py/NOTES
8
py/NOTES
|
|
@ -6,3 +6,11 @@ NOTE: currently my "primitive roots" are actually just the numbers that generate
|
|||
|
||||
This site is useful as a reference:
|
||||
https://owlsmath.neocities.org/Primitive%20Root%20Calculator/calculator
|
||||
|
||||
|
||||
2. Make Noether into a mathematical programming language (like MATLAB),
|
||||
with the ability to create custom types (ie the integers modulo n)
|
||||
with specific properties like operator overloading. Then you could
|
||||
create functions that check whether a set and an operator form a
|
||||
group or not, etc.
|
||||
2.1 Use ^ to indicate exponentiation, and use ^| to indicate xor
|
||||
|
|
|
|||
107
py/m.py
Normal file
107
py/m.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#!/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
|
||||
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 = []
|
||||
|
||||
# calculate left padding to align i values
|
||||
# lpadded = strlen_n
|
||||
# calculate right padding to align len(orb) values
|
||||
rpadded = len(str(phi))
|
||||
|
||||
# find all invertible elements (skipped for now)
|
||||
for a in range(n):
|
||||
orb, ord, periodic = orbit(a, n)
|
||||
|
||||
# check if `a` is a primitive root
|
||||
# NOTE: this should actually be `proot = (ord == phi(n))`
|
||||
# proot = (ord + 1 == n)
|
||||
proot = (ord == phi)
|
||||
proot_c += proot
|
||||
|
||||
# calculate padding
|
||||
lpad = ' ' * (strlen_n - len(str(a)))
|
||||
rpad = ' ' * (rpadded - len(str(ord)))
|
||||
# calculate coloring
|
||||
color_a = Color.Red
|
||||
color_ord = None
|
||||
color_orb = None
|
||||
style_ord = 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('->', end=' ', flush=False)
|
||||
uprint(f'{ord}{rpad}', color=color_ord, style=style_ord, end=' ', flush=False)
|
||||
uprint('|', end=' ', flush=False)
|
||||
uprint(f'{orb}', color=color_orb, style=style_orb, flush=True)
|
||||
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,6 +1,7 @@
|
|||
# Modulo Test
|
||||
from prbraid.math import *
|
||||
from prbraid.color import *
|
||||
from noether.lib.groups import *
|
||||
from noether.cli.ansi import *
|
||||
from noether.cli._old import *
|
||||
|
||||
import sys
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ def main():
|
|||
n = None
|
||||
strlen_n = None
|
||||
try:
|
||||
uprint(PROMPT, color=Color.Blue, end='')
|
||||
uprint(PROMPT, color=Color.BLUE, end='')
|
||||
n = input()
|
||||
strlen_n = len(n)
|
||||
n = int(n)
|
||||
|
|
@ -43,7 +44,7 @@ def main():
|
|||
|
||||
# find all invertible elements (skipped for now)
|
||||
for a in range(n):
|
||||
orb, ord = orbit(a, n)
|
||||
orb, ord, periodic = cyclic_subgrp(a, n)
|
||||
# check if `a` is a primitive root
|
||||
proot = (ord + 1 == n)
|
||||
proot_c += proot
|
||||
|
|
|
|||
119
py/noether.py
119
py/noether.py
|
|
@ -1,105 +1,34 @@
|
|||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import readline
|
||||
from noether.cli.style import *
|
||||
from noether.cli.prompt import *
|
||||
from noether.lib.structs import Result
|
||||
|
||||
class Noether(Prompt):
|
||||
DEFAULT_PROMPT = style('~>> ', Color.BLUE)
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
from noether.math import *
|
||||
from noether.cli import *
|
||||
def _parse(self, command: str) -> int:
|
||||
try:
|
||||
return Result.succeed(int(command))
|
||||
except ValueError:
|
||||
return Result.fail('Not an integer.')
|
||||
|
||||
|
||||
PROMPT = '[n]: '
|
||||
|
||||
'''
|
||||
Pairwise orbit cumulative summation (cum orb)
|
||||
'''
|
||||
def orb_cum(cum, orb):
|
||||
for i in range(len(cum)):
|
||||
cum[i] += orb[i]
|
||||
def _exec(self, command: int) -> None:
|
||||
print(style(f'OMG {command}', Color.CYAN))
|
||||
|
||||
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
|
||||
try:
|
||||
noether = Noether()
|
||||
while True:
|
||||
noether.prompt()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
err = style('Exit Requested...', Effect.ITALICS)
|
||||
print('\n', style('[!]', Color.RED), err)
|
||||
|
||||
# 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 = []
|
||||
|
||||
# calculate left padding to align i values
|
||||
# lpadded = strlen_n
|
||||
# calculate right padding to align len(orb) values
|
||||
rpadded = len(str(phi))
|
||||
|
||||
# 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
|
||||
|
||||
# calculate padding
|
||||
lpad = ' ' * (strlen_n - len(str(a)))
|
||||
rpad = ' ' * (rpadded - len(str(ord)))
|
||||
# calculate coloring
|
||||
color_a = Color.Red
|
||||
color_ord = None
|
||||
color_orb = None
|
||||
style_ord = 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('->', end=' ', flush=False)
|
||||
uprint(f'{ord}{rpad}', color=color_ord, style=style_ord, end=' ', flush=False)
|
||||
uprint('|', end=' ', flush=False)
|
||||
uprint(f'{orb}', color=color_orb, style=style_orb, flush=True)
|
||||
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
|
||||
# handles premature SIGINT/EOF
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
class Color(Enum):
|
||||
Reset = 0
|
||||
Bold = 1
|
||||
Italics = 2
|
||||
Underline = 4
|
||||
|
||||
Black = 30
|
||||
Red = 31
|
||||
Green = 32
|
||||
Yellow = 33
|
||||
Blue = 34
|
||||
Magenta = 35
|
||||
Cyan = 36
|
||||
White = 37
|
||||
|
||||
# escape sequence format string
|
||||
__fescseq = '\033[{0}'
|
||||
# ansi reset code
|
||||
__ansi_rst = '\033[0m'
|
||||
|
||||
@staticmethod
|
||||
def _ansi_ret(ansi):
|
||||
return ansi, Color.__ansi_white
|
||||
|
||||
@staticmethod
|
||||
def code(color: 'Color'):
|
||||
return Color.__fescseq.format(f'{color.value}m')
|
||||
|
||||
# move the cursor up n lines
|
||||
@staticmethod
|
||||
def mvup_code(n: int):
|
||||
return Color.__fescseq.format(f'{n}A')
|
||||
|
||||
def ansi(self):
|
||||
code = Color.code(self)
|
||||
if self == Color.White:
|
||||
return code, ''
|
||||
return code, Color.__ansi_rst
|
||||
|
||||
# !! ULTRA PRINT !!
|
||||
def uprint(text: str,
|
||||
color: Optional[Color] = None,
|
||||
style: Optional[Color] = None,
|
||||
moveup: int = 0,
|
||||
end: str = '\n',
|
||||
flush: bool = True):
|
||||
if color is not None:
|
||||
c = color.ansi()
|
||||
text = f'{c[0]}{text}{c[1]}'
|
||||
if style is not None:
|
||||
s = style.ansi()
|
||||
tail = '' if (c is not None and c[1] == '') else s[1]
|
||||
text = f'{s[0]}{text}{tail}'
|
||||
if moveup > 0:
|
||||
text = Color.mvup_code(moveup) + text
|
||||
print(text, end=end, flush=flush)
|
||||
0
py/noether/cli/__init__.py
Normal file
0
py/noether/cli/__init__.py
Normal file
66
py/noether/cli/prompt.py
Normal file
66
py/noether/cli/prompt.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
'''
|
||||
===== Prompt Handling =====
|
||||
This file implements the Prompt class, allowing for
|
||||
inheritance and instantiation of Prompt objects.
|
||||
The CLI class uses these instead of containing the
|
||||
logic immediately within itself.
|
||||
'''
|
||||
|
||||
from typing import Any
|
||||
|
||||
from noether.cli.style import *
|
||||
from noether.lib.structs import Result
|
||||
|
||||
class Prompt:
|
||||
DEFAULT_PROMPT = 'DEF> '
|
||||
def __init__(self) -> None:
|
||||
self._prompt = self.DEFAULT_PROMPT
|
||||
|
||||
'''
|
||||
Prompt and await command via stdin.
|
||||
'''
|
||||
def prompt(self):
|
||||
command = self.__request()
|
||||
result = self._parse(command)
|
||||
if result.success:
|
||||
self._exec(result.value)
|
||||
else:
|
||||
self.__parse_error(result)
|
||||
|
||||
'''
|
||||
!! OVERRIDE ON INHERITANCE !!
|
||||
Handles the parsing of a given command.
|
||||
'''
|
||||
def _parse(self, command: str) -> Result:
|
||||
return Result.succeed(command)
|
||||
|
||||
def __parse_error(self, error: Result) -> None:
|
||||
err = f' ↳ {style(error.reason, Effect.ITALICS)}'
|
||||
print(style(err, Effect.DIM))
|
||||
|
||||
'''
|
||||
!! OVERRIDE ON INHERITANCE !!
|
||||
Handles the execution of a command that
|
||||
was successfully parsed by a Prompt inheritor.
|
||||
'''
|
||||
def _exec(self, command: Any) -> None:
|
||||
pass
|
||||
|
||||
'''
|
||||
Internal use only. Handles a raw request with no validation.
|
||||
'''
|
||||
def __request(self) -> None:
|
||||
print(self.__get_prompt(), end='', flush=True)
|
||||
return input()
|
||||
|
||||
'''
|
||||
!! OVERRIDE ON INHERITANCE !!
|
||||
'''
|
||||
def _style_prompt(self, prompt: str) -> str:
|
||||
return prompt
|
||||
|
||||
def __get_prompt(self) -> str:
|
||||
return self._style_prompt(self._prompt)
|
||||
|
||||
def set_prompt(self, prompt: str) -> None:
|
||||
self._prompt = prompt
|
||||
|
|
@ -8,8 +8,12 @@ for management of the CLI.
|
|||
from sys import stdout
|
||||
from enum import StrEnum
|
||||
|
||||
# Removes ALL set colors/styling
|
||||
RESET = '\x1b[0m'
|
||||
|
||||
'''
|
||||
Implements text foreground coloring.
|
||||
'''
|
||||
class Color(StrEnum):
|
||||
BLACK = '\x1b[30m'
|
||||
RED = '\x1b[31m'
|
||||
|
|
@ -20,7 +24,18 @@ class Color(StrEnum):
|
|||
CYAN = '\x1b[36m'
|
||||
WHITE = '\x1b[37m'
|
||||
|
||||
class Style(StrEnum):
|
||||
'''
|
||||
Handles the application of a Color to a given text.
|
||||
Unset the `temp` flag to leave this style on (after the given text)
|
||||
'''
|
||||
@staticmethod
|
||||
def apply(color: 'Color', text: str, temp: bool = True) -> str:
|
||||
return color + text + Color.WHITE if temp else ''
|
||||
|
||||
'''
|
||||
Implements text effects.
|
||||
'''
|
||||
class Effect(StrEnum):
|
||||
BOLD = '\x1b[1m'
|
||||
DIM = '\x1b[2m'
|
||||
ITALICS = '\x1b[3m'
|
||||
|
|
@ -29,14 +44,39 @@ class Style(StrEnum):
|
|||
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
|
||||
]
|
||||
'''
|
||||
Returns the opposite ESC code to a given Effect.
|
||||
ie the opposite of BOLD '\x1b[1m]' is '\x1b[21m'
|
||||
'''
|
||||
@staticmethod
|
||||
def _inverse(effect: 'Effect') -> str:
|
||||
return f'{effect[:2]}2{effect[2:]}'
|
||||
|
||||
'''
|
||||
Handles the application of a Effect to a given text.
|
||||
Unset the `temp` flag to leave this style on (after the given text)
|
||||
'''
|
||||
@staticmethod
|
||||
def apply(effect: 'Effect', text: str, temp: bool = True) -> str:
|
||||
return effect + text + Effect._inverse(effect) if temp else ''
|
||||
|
||||
'''
|
||||
Applies styling (color/effects/etc) to a given string
|
||||
Unset the `temp` flag to leave this style on (after the given text)
|
||||
'''
|
||||
def style(text: str, *args: Color | Effect, temp: bool = True) -> str:
|
||||
# unsures we don't add redundant "set white" commands
|
||||
unset_color = False
|
||||
for arg in args:
|
||||
unset = temp
|
||||
if isinstance(arg, Color):
|
||||
unset_color |= temp
|
||||
unset = False
|
||||
text = type(arg).apply(arg, text, temp=True)
|
||||
if unset_color:
|
||||
text += Color.WHITE
|
||||
return text
|
||||
|
||||
|
||||
'''
|
||||
Implements cursor movement functionality.
|
||||
|
|
@ -48,8 +88,8 @@ NOTE:
|
|||
'''
|
||||
class Cursor(StrEnum):
|
||||
# SAVE current / RESTORE last saved cursor position
|
||||
SAVE = f'\x1b[7'
|
||||
RESTORE = f'\x1b[8'
|
||||
_SAVE = f'\x1b[7'
|
||||
_RESTORE = f'\x1b[8'
|
||||
|
||||
_MV_UP = 'A'
|
||||
_MV_DOWN = 'B'
|
||||
|
|
@ -137,7 +177,7 @@ class Cursor(StrEnum):
|
|||
'''
|
||||
@staticmethod
|
||||
def save() -> None:
|
||||
stdout.write(Cursor.SAVE)
|
||||
stdout.write(Cursor._SAVE)
|
||||
|
||||
'''
|
||||
Restores the cursor position to a saved position.
|
||||
|
|
@ -145,7 +185,7 @@ class Cursor(StrEnum):
|
|||
'''
|
||||
@staticmethod
|
||||
def restore() -> None:
|
||||
stdout.write(Cursor.RESTORE)
|
||||
stdout.write(Cursor._RESTORE)
|
||||
|
||||
'''
|
||||
Handles erasing content displayed on the screen.
|
||||
|
|
@ -155,18 +195,40 @@ 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
|
||||
_SCR_AFTER = '\x1b[0J'
|
||||
_SCR_BEFORE = '\x1b[1J'
|
||||
_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'
|
||||
_LINE_AFTER = '\x1b[0K'
|
||||
_LINE_BEFORE = '\x1b[1K'
|
||||
_LINE_ALL = '\x1b[2K'
|
||||
|
||||
'''
|
||||
Erase characters on the entire screen.
|
||||
Set `before` flag to only erase before the cursor,
|
||||
set `after` flag to only erase after the cursor.
|
||||
'''
|
||||
@staticmethod
|
||||
# TODO COME BACK HERE
|
||||
def screen(before: bool = False, after: bool = False) -> None:
|
||||
if before:
|
||||
stdout.write(Erase._SCR_BEFORE)
|
||||
elif after:
|
||||
stdout.write(Erase._SCR_AFTER)
|
||||
else:
|
||||
stdout.write(Erase._SCR_ALL)
|
||||
|
||||
'''
|
||||
Erase characters on the current line.
|
||||
Set `before` flag to only erase before the cursor,
|
||||
set `after` flag to only erase after the cursor.
|
||||
'''
|
||||
@staticmethod
|
||||
def line(before: bool = False, after: bool = False) -> None:
|
||||
if before:
|
||||
stdout.write(Erase._LINE_BEFORE)
|
||||
elif after:
|
||||
stdout.write(Erase._LINE_AFTER)
|
||||
else:
|
||||
stdout.write(Erase._LINE_ALL)
|
||||
0
py/noether/cmd/__init__.py
Normal file
0
py/noether/cmd/__init__.py
Normal file
60
py/noether/cmd/cycsub.py
Normal file
60
py/noether/cmd/cycsub.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
from sys import stdout
|
||||
|
||||
from noether.cli.style import *
|
||||
from noether.cli.prompt import *
|
||||
from noether.lib.structs import Result
|
||||
|
||||
from noether.lib.util import digits
|
||||
from noether.lib.groups import cyclic_subgrp
|
||||
from noether.lib.primes import totient, is_prime
|
||||
|
||||
class cycsub(Prompt):
|
||||
DEFAULT_PROMPT = style('[n]: ', Color.BLUE)
|
||||
def __init__(self, ignore_zero: bool = True) -> None:
|
||||
super().__init__()
|
||||
self.ignore_zero = ignore_zero
|
||||
|
||||
def _parse(self, command: str) -> int:
|
||||
try:
|
||||
return Result.succeed(int(command))
|
||||
except ValueError:
|
||||
return Result.fail('Not an integer.')
|
||||
|
||||
def _exec(self, n: int) -> None:
|
||||
phi = totient(n)
|
||||
lpadding = digits(n)
|
||||
rpadding = digits(phi)
|
||||
if is_prime(n, phi=n):
|
||||
Cursor.save()
|
||||
Cursor.move_y(-1, reset=False)
|
||||
Cursor.set_x(len(self.DEFAULT_PROMPT) + lpadding + 1)
|
||||
stdout.write(style('[PRIME]', Color.MAGENTA, Effect.BOLD))
|
||||
Cursor.restore()
|
||||
stdout.flush()
|
||||
|
||||
# keeps track of all primitive roots
|
||||
# (note that there will be exactly totient(phi) of them)
|
||||
proots = []
|
||||
for g in range(n):
|
||||
G, order, periodic = cyclic_subgrp(g, n, ignore_zero=self.ignore_zero)
|
||||
primitive = (order == phi) # primitive root
|
||||
|
||||
lpad = ' ' * (lpadding - digits(a))
|
||||
rpad = ' ' * (rpadding - digits(order))
|
||||
|
||||
color_g = Color.RED
|
||||
style_G = []
|
||||
style_order = []
|
||||
if primitive:
|
||||
color_g = Color.GREEN
|
||||
style_G = [Color.GREEN, Effect.BOLD]
|
||||
style_order = [Color.YELLOW, Effect.BOLD]
|
||||
proots.append(g)
|
||||
elif gcd(g, n) == 1:
|
||||
color_g = Color.Yellow
|
||||
line = style(f'{lpad}{g}', color_g, Effect.BOLD) + \
|
||||
'-> ' + \
|
||||
style(f'{order}{rpad} ', *style_order) + \
|
||||
'| ' + \
|
||||
style(str(G), *style_G)
|
||||
print(line, flush=True)
|
||||
3
py/noether/exceptions.py
Normal file
3
py/noether/exceptions.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
class PromptParseError(Exception):
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
37
py/noether/lib/groups.py
Normal file
37
py/noether/lib/groups.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
'''
|
||||
This library exists to isolate all math functions
|
||||
related to groups and their representations.
|
||||
'''
|
||||
|
||||
from math import gcd
|
||||
|
||||
'''
|
||||
Returns the multiplicative cyclic subgroup
|
||||
generated by an element g modulo m.
|
||||
Returns the cyclic subgroup as a list[int],
|
||||
the order of that subgroup, and a boolean
|
||||
indicating whether g is infinitely repeating
|
||||
with period == ord<g> (or otherwise if it
|
||||
terminates with g**ord<g> == 0).
|
||||
'''
|
||||
def cyclic_subgrp(g: int,
|
||||
m: int,
|
||||
ignore_zero: bool = True) -> tuple[list[int], int, bool]:
|
||||
G = []
|
||||
order = 0
|
||||
periodic = True
|
||||
a = 1 # start at identity
|
||||
for _ in range(m):
|
||||
a = (a * g) % m
|
||||
if a == 0:
|
||||
if not ignore_zero:
|
||||
G.append(a)
|
||||
order += 1
|
||||
periodic = False
|
||||
break
|
||||
# check if we've reached something periodic
|
||||
elif a in G[:1]:
|
||||
break
|
||||
G.append(a)
|
||||
order += 1
|
||||
return G, order, periodic
|
||||
36
py/noether/lib/primes.py
Normal file
36
py/noether/lib/primes.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
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
|
||||
|
||||
'''
|
||||
Tests the primality of an integer using its totient.
|
||||
NOTE: If totient(n) has already been calculated
|
||||
then pass it as the optional phi parameter.
|
||||
'''
|
||||
def is_prime(n: int, phi: int = None):
|
||||
return n - 1 == phi if phi is not None else totient(n):
|
||||
|
||||
'''
|
||||
Prime number generator function.
|
||||
Returns the tuple (p, phi(p)) where p is prime
|
||||
and phi is Euler's totient function.
|
||||
'''
|
||||
def prime_gen():
|
||||
n = 1
|
||||
while True:
|
||||
n += 1
|
||||
phi = totient(n)
|
||||
if is_prime(n, phi=phi):
|
||||
return n, phi
|
||||
1
py/noether/lib/structs/__init__.py
Normal file
1
py/noether/lib/structs/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from noether.lib.structs.__result import Result
|
||||
19
py/noether/lib/structs/__result.py
Normal file
19
py/noether/lib/structs/__result.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
class Result:
|
||||
def __init__(self,
|
||||
success: bool,
|
||||
reason: str,
|
||||
value: Optional[any] = None) -> None:
|
||||
self.success = success
|
||||
self.reason = reason
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def succeed(cls, value: any, reason: str = 'Ok') -> 'Result':
|
||||
return cls(True, reason, value=value)
|
||||
|
||||
@classmethod
|
||||
def fail(cls, reason: str, value: Optional[any] = None) -> 'Result':
|
||||
return cls(False, reason, value=value)
|
||||
2
py/noether/lib/util.py
Normal file
2
py/noether/lib/util.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
def digits(n: int) -> int:
|
||||
return len(str(n))
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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
80
py/primes.py
|
|
@ -1,80 +0,0 @@
|
|||
# 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