begin shift to nim code base
This commit is contained in:
parent
33bcffdc69
commit
4b20f9961b
25 changed files with 625 additions and 303 deletions
103
lang/demo/cycgrp.no
Normal file
103
lang/demo/cycgrp.no
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
Type Traits (TTrait)
|
||||
Operation Traits (OTrait)
|
||||
Types (implement TTraits)
|
||||
Operations on types (implement OTraits)
|
||||
*/
|
||||
|
||||
trait QuotientRemainder<T: Any>:
|
||||
proc quotient(a: T, b: T): T
|
||||
proc remainder(a: T, b: T): T
|
||||
|
||||
induce QuotientRemainder on Self: Any having
|
||||
`+`<Self>: BinOp | Invertible | UniqueIdentity,
|
||||
`<`<Self>: POrder, // NOTE: PartialOrders implement BinOp
|
||||
`==`<Self>: EqRel -> // NOTE: EqRels implement BinOp
|
||||
// `let` is a macro that replaces all instances following code's
|
||||
// AST with whatever values were defined via alias
|
||||
// (it does NOT change scope at all)
|
||||
with (
|
||||
// define aliases for the inverse of an element as both a
|
||||
// prefixed unary operation and a infix binary operation
|
||||
preunary `-`(a: Self) -> +.inverse(a) // -a = +(inverse(a))
|
||||
binop `-`(a: Self, b: Self) -> a + +.inverse(b) // a - b = a +(inverse(b))
|
||||
// define aliases for <= by simply inducing <= from < and = on Self
|
||||
binop `<=`(a: Self, b: Self) -> a == b or a < b
|
||||
):
|
||||
// NOTE: any proc for a trait can be overriden, the standard definitions
|
||||
// NOTE: are just the standard "induced" definitions
|
||||
|
||||
proc quotient(a: Self, b: Self): Self ->
|
||||
var quotient: Integer = 1
|
||||
alias delta -> b
|
||||
while not (delta <= b):
|
||||
delta = delta - b
|
||||
quotient++
|
||||
return quotient
|
||||
|
||||
// NOTE: assumes a is passed by value not by reference
|
||||
proc remainder(a: Self, b: Self): Self ->
|
||||
alias delta -> a // compiler alias
|
||||
while not (delta <= b):
|
||||
delta = delta - b
|
||||
return delta
|
||||
|
||||
// define the standard induce of Modulo via QuotientRemainder
|
||||
induce Modulo on T: QuotientRemainder ->
|
||||
binop mod(a: T, b: T): T -> T.remainder(a, b)
|
||||
|
||||
// NOTE: the Slicable trait allow us to take slices ie `1..5`
|
||||
type CycGrp<T: Modulo | Slicable having `+`<T>: BinOp|Invertible|UniqueIdentity>: Group<T> ->
|
||||
// intrinsic variables
|
||||
intrinsic modulus: T
|
||||
intrinsic elements : Set<T>
|
||||
|
||||
structure(modulus: T) ->
|
||||
self.modulus = modulus
|
||||
// the following line is a generalised implementation of `1..(self.modulus-1)`
|
||||
self.elements = (self.modulus + +.inverse(self.modulus))..(self.modulus + +.inverse(+.identity))
|
||||
|
||||
// "functors" allow one type to be represented as another type
|
||||
// aka functors == typecasts
|
||||
functor(value: T) -> value mod self.modulus
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* My attempt at formalising rings and fields in Noether
|
||||
* NOTE: the following traits are "formed" from a specific structure
|
||||
* NOTE: they have properties (`prop`) which are just aliases to
|
||||
* NOTE: the structure that formed them
|
||||
*/
|
||||
trait Magma<T: Any> on (SET: Set<T>, ACTION: BinOp<T>) ->
|
||||
prop SET -> SET
|
||||
prop ACTION -> ACTION
|
||||
|
||||
trait SemiGroup<T: Any>
|
||||
on (M: Magma<T>)
|
||||
having M#ACTION: Associative ->
|
||||
inherit M#SET, M#ACTION
|
||||
|
||||
trait Group<T: Any>
|
||||
on (SG: SemiGroup<T>)
|
||||
having SG#ACTION: UniqueIdentity | Invertible ->
|
||||
inherit SG#SET, SG#ACTION
|
||||
|
||||
trait Ring<T: Any>
|
||||
on (SET: Set<T>, ADD: BinOp<T>, MUL: BinOp<T>)
|
||||
having (SET, ADD) : Group<T>,
|
||||
(SET, MUL) : SemiGroup<T>,
|
||||
(ADD, MUL) : Distributive ->
|
||||
prop SET -> SET
|
||||
prop ADD -> ADD
|
||||
prop MUL -> MUL
|
||||
|
||||
/*
|
||||
* Notation: (`->` as `return`)
|
||||
* x() -> expr
|
||||
* // same as:
|
||||
* x():
|
||||
* return expr
|
||||
*/
|
||||
14
noether.nimble
Normal file
14
noether.nimble
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Emile Clark-Boman"
|
||||
description = "Type theoretic imperative and logic language for mathematical programming"
|
||||
license = "MIT"
|
||||
srcDir = "src"
|
||||
installExt = @["nim"]`
|
||||
bin = @["noether"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 2.2.0"
|
||||
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
|
||||
7
src/noether.nim
Normal file
7
src/noether.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# This is just an example to get you started. A typical hybrid package
|
||||
# uses this file as the main entry point of the application.
|
||||
|
||||
import noether/submodule
|
||||
|
||||
when isMainModule:
|
||||
echo(getWelcomeMessage())
|
||||
29
src/noether/lexer.nim
Normal file
29
src/noether/lexer.nim
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import std/streams
|
||||
|
||||
type
|
||||
nlLexer* = object
|
||||
stream: Stream
|
||||
pos: Natural
|
||||
|
||||
proc newLexerFromStream(stream: Stream): nlLexer =
|
||||
result = nlLexer(
|
||||
stream: stream,
|
||||
pos: 0,
|
||||
)
|
||||
)
|
||||
|
||||
proc newLexer*(content: string, isFile: bool): nlLexer =
|
||||
result = newLexerFromStream(
|
||||
streamFile(content) if isFile else streamString(content)
|
||||
)
|
||||
)
|
||||
|
||||
proc streamFile(filename: string): FileStream =
|
||||
result = newFileStream(filename, fmRead)
|
||||
|
||||
proc streamString(str: string): StringStream =
|
||||
result = newStringStream(str)
|
||||
|
||||
|
||||
proc nextToken*(lexer: nlLexer): nlToken =
|
||||
result = newToken[]
|
||||
6
src/noether/submodule.nim
Normal file
6
src/noether/submodule.nim
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# This is just an example to get you started. Users of your hybrid library will
|
||||
# import this file by writing ``import srcpkg/submodule``. Feel free to rename or
|
||||
# remove this file altogether. You may create additional modules alongside
|
||||
# this file as required.
|
||||
|
||||
proc getWelcomeMessage*(): string = "Hello, World!"
|
||||
1
tests/config.nims
Normal file
1
tests/config.nims
Normal file
|
|
@ -0,0 +1 @@
|
|||
switch("path", "$projectDir/../src")
|
||||
12
tests/test1.nim
Normal file
12
tests/test1.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# This is just an example to get you started. You may wish to put all of your
|
||||
# tests into a single file, or separate them into multiple `test1`, `test2`
|
||||
# etc. files (better names are recommended, just make sure the name starts with
|
||||
# the letter 't').
|
||||
#
|
||||
# To run these tests, simply execute `nimble test`.
|
||||
|
||||
import unittest
|
||||
|
||||
import src/submodule
|
||||
test "correct welcome":
|
||||
check getWelcomeMessage() == "Hello, World!"
|
||||
Loading…
Add table
Add a link
Reference in a new issue