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:
|
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
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
|
# 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
|
||||||
|
|
|
||||||
119
py/noether.py
119
py/noether.py
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 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)
|
||||||
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