begin shift to nim code base

This commit is contained in:
Emile Clark-Boman 2025-06-16 20:47:52 +10:00
parent 33bcffdc69
commit 4b20f9961b
25 changed files with 625 additions and 303 deletions

103
lang/demo/cycgrp.no Normal file
View 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
View 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"

View file

@ -6,3 +6,11 @@ NOTE: currently my "primitive roots" are actually just the numbers that generate
This site is useful as a reference: This site is useful as a reference:
https://owlsmath.neocities.org/Primitive%20Root%20Calculator/calculator 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
View 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

View file

@ -1,6 +1,7 @@
# Modulo Test # Modulo Test
from prbraid.math import * from noether.lib.groups import *
from prbraid.color import * from noether.cli.ansi import *
from noether.cli._old import *
import sys import sys
@ -18,7 +19,7 @@ def main():
n = None n = None
strlen_n = None strlen_n = None
try: try:
uprint(PROMPT, color=Color.Blue, end='') uprint(PROMPT, color=Color.BLUE, end='')
n = input() n = input()
strlen_n = len(n) strlen_n = len(n)
n = int(n) n = int(n)
@ -43,7 +44,7 @@ def main():
# find all invertible elements (skipped for now) # find all invertible elements (skipped for now)
for a in range(n): for a in range(n):
orb, ord = orbit(a, n) orb, ord, periodic = cyclic_subgrp(a, n)
# check if `a` is a primitive root # check if `a` is a primitive root
proot = (ord + 1 == n) proot = (ord + 1 == n)
proot_c += proot proot_c += proot

View file

@ -1,105 +1,34 @@
#!/usr/bin/env python3 from noether.cli.style import *
import sys from noether.cli.prompt import *
import readline from noether.lib.structs import Result
class Noether(Prompt):
DEFAULT_PROMPT = style('~>> ', Color.BLUE)
def __init__(self) -> None:
super().__init__()
from noether.math import * def _parse(self, command: str) -> int:
from noether.cli import * try:
return Result.succeed(int(command))
except ValueError:
return Result.fail('Not an integer.')
def _exec(self, command: int) -> None:
PROMPT = '[n]: ' print(style(f'OMG {command}', Color.CYAN))
'''
Pairwise orbit cumulative summation (cum orb)
'''
def orb_cum(cum, orb):
for i in range(len(cum)):
cum[i] += orb[i]
def main(): def main():
while True: try:
n = None noether = Noether()
strlen_n = None while True:
try: noether.prompt()
uprint(PROMPT, color=Color.Blue, end='') except (KeyboardInterrupt, EOFError):
n = input() err = style('Exit Requested...', Effect.ITALICS)
strlen_n = len(n) print('\n', style('[!]', Color.RED), err)
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 = 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__': if __name__ == '__main__':
try: try:
main() main()
except (KeyboardInterrupt, EOFError): except (KeyboardInterrupt, EOFError):
pass # handles premature SIGINT/EOF
pass

View file

@ -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)

View file

66
py/noether/cli/prompt.py Normal file
View 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

View file

@ -8,8 +8,12 @@ for management of the CLI.
from sys import stdout from sys import stdout
from enum import StrEnum from enum import StrEnum
# Removes ALL set colors/styling
RESET = '\x1b[0m' RESET = '\x1b[0m'
'''
Implements text foreground coloring.
'''
class Color(StrEnum): class Color(StrEnum):
BLACK = '\x1b[30m' BLACK = '\x1b[30m'
RED = '\x1b[31m' RED = '\x1b[31m'
@ -20,7 +24,18 @@ class Color(StrEnum):
CYAN = '\x1b[36m' CYAN = '\x1b[36m'
WHITE = '\x1b[37m' 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' BOLD = '\x1b[1m'
DIM = '\x1b[2m' DIM = '\x1b[2m'
ITALICS = '\x1b[3m' ITALICS = '\x1b[3m'
@ -29,14 +44,39 @@ class Style(StrEnum):
REVERSE = '\x1b[7m' REVERSE = '\x1b[7m'
HIDE = '\x1b[8m' HIDE = '\x1b[8m'
_DISABLE = [ '''
'\x1b[21m', # BOLD Returns the opposite ESC code to a given Effect.
'\x1b[22m', # DIM ie the opposite of BOLD '\x1b[1m]' is '\x1b[21m'
'\x1b[24m', # UNDERLINE '''
'\x1b[25m', # BLINK @staticmethod
'\x1b[27m', # REVERSE def _inverse(effect: 'Effect') -> str:
'\x1b[28m' # HIDE 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. Implements cursor movement functionality.
@ -48,8 +88,8 @@ NOTE:
''' '''
class Cursor(StrEnum): class Cursor(StrEnum):
# SAVE current / RESTORE last saved cursor position # SAVE current / RESTORE last saved cursor position
SAVE = f'\x1b[7' _SAVE = f'\x1b[7'
RESTORE = f'\x1b[8' _RESTORE = f'\x1b[8'
_MV_UP = 'A' _MV_UP = 'A'
_MV_DOWN = 'B' _MV_DOWN = 'B'
@ -137,7 +177,7 @@ class Cursor(StrEnum):
''' '''
@staticmethod @staticmethod
def save() -> None: def save() -> None:
stdout.write(Cursor.SAVE) stdout.write(Cursor._SAVE)
''' '''
Restores the cursor position to a saved position. Restores the cursor position to a saved position.
@ -145,7 +185,7 @@ class Cursor(StrEnum):
''' '''
@staticmethod @staticmethod
def restore() -> None: def restore() -> None:
stdout.write(Cursor.RESTORE) stdout.write(Cursor._RESTORE)
''' '''
Handles erasing content displayed on the screen. Handles erasing content displayed on the screen.
@ -155,18 +195,40 @@ via these sequences. The \r code should be given after.
class Erase(StrEnum): class Erase(StrEnum):
# erase everything from the current cursor # erase everything from the current cursor
# position to the START/END of the screen # position to the START/END of the screen
ER_SCR_END = '\x1b[0J' _SCR_AFTER = '\x1b[0J'
ER_SCR_START = '\x1b[1J' _SCR_BEFORE = '\x1b[1J'
# ER_SCR_ALL = '\x1b[2J' _SCR_ALL = '\x1b[3J' # erase screen and delete all saved cursors
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 # erase everything from the current cursor
# position to the START/END of the current line # position to the START/END of the current line
ER_LINE_END = '\x1b[0K' _LINE_AFTER = '\x1b[0K'
ER_LINE_START = '\x1b[1K' _LINE_BEFORE = '\x1b[1K'
ER_LINE_ALL = '\x1b[2K' _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 @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)

View file

60
py/noether/cmd/cycsub.py Normal file
View 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
View 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
View 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
View 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

View file

@ -0,0 +1 @@
from noether.lib.structs.__result import Result

View 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
View file

@ -0,0 +1,2 @@
def digits(n: int) -> int:
return len(str(n))

View file

@ -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

View file

@ -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
View 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
View 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[]

View 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
View file

@ -0,0 +1 @@
switch("path", "$projectDir/../src")

12
tests/test1.nim Normal file
View 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!"