├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bin ├── compile.py ├── z++ └── zcc ├── lib ├── Makefile ├── README.md ├── TODO.md ├── package-lock.json ├── package.json ├── src │ ├── arith-export.zig │ ├── arith.ts │ ├── arith.zig │ ├── arith │ │ ├── generic-power.ts │ │ └── generic-power.zig │ ├── bench │ │ └── fib │ │ │ ├── fib.jl │ │ │ ├── fib.js │ │ │ ├── fib.py │ │ │ ├── fib.rb │ │ │ └── fib.zig │ ├── complex │ │ ├── complex.ts │ │ └── complex.zig │ ├── custom-allocator.zig │ ├── errors.zig │ ├── factor-export.zig │ ├── factor.ts │ ├── factor.zig │ ├── flint │ │ ├── interface.zig │ │ └── nmod-mat.zig │ ├── index.ts │ ├── integer │ │ ├── index.ts │ │ ├── integer-ring.ts │ │ ├── integer.test.ts │ │ ├── integer.ts │ │ ├── integer.zig │ │ └── interface.zig │ ├── interface.zig │ ├── interface │ │ ├── allocator.zig │ │ ├── gmp.zig │ │ ├── proxy.zig │ │ └── util.zig │ ├── misc │ │ ├── index.ts │ │ └── prod.ts │ ├── modular │ │ ├── contfrac.zig │ │ ├── dense-matrix-interface.zig │ │ ├── dense-matrix.ts │ │ ├── dense-matrix.zig │ │ ├── dense-vector-interface.zig │ │ ├── dense-vector.ts │ │ ├── dense-vector.zig │ │ ├── dims.test.ts │ │ ├── dims.ts │ │ ├── dims.zig │ │ ├── elliptic-curve-interface.zig │ │ ├── elliptic-curve-test.ts │ │ ├── elliptic-curve.test.ts │ │ ├── elliptic-curve.ts │ │ ├── elliptic-curve.zig │ │ ├── heilbronn.zig │ │ ├── interface.zig │ │ ├── manin-symbols.ts │ │ ├── manin-symbols.zig │ │ ├── mat2x2.zig │ │ ├── modsym-2term.zig │ │ ├── p1list-export.zig │ │ ├── p1list.test.ts │ │ ├── p1list.ts │ │ ├── p1list.zig │ │ ├── sl2z.zig │ │ ├── sparse-matrix-interface.zig │ │ ├── sparse-matrix.zig │ │ ├── sparse-vector-interface.zig │ │ └── sparse-vector.zig │ ├── number.ts │ ├── pari │ │ ├── index.ts │ │ ├── interface.zig │ │ ├── pari.h │ │ ├── pari.test.ts │ │ └── pari.zig │ ├── python │ │ ├── index.ts │ │ ├── interface.zig │ │ ├── python.test-disabled.ts │ │ └── python.zig │ ├── random.zig │ ├── rational │ │ ├── index.ts │ │ ├── interface.zig │ │ ├── rational-field.ts │ │ ├── rational.ts │ │ └── rational.zig │ ├── time.zig │ ├── timer.zig │ ├── trial-division.zig │ ├── util.zig │ ├── wasm.ts │ └── zig-test.sh └── tsconfig.json └── packages ├── eclib └── Makefile ├── flint ├── Makefile └── patches │ └── ulong.patch ├── gf2x └── Makefile ├── gmp ├── Makefile ├── README.md ├── package-lock.json ├── package.json └── patches │ └── long-long-abi.patch ├── jpython ├── .npmignore ├── .prettierignore ├── .yapfignore ├── CONTRIBUTORS.md ├── HACKING.md ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── bench │ ├── README.md │ ├── all.py │ ├── bench.py │ ├── brython.py │ ├── call.py │ ├── fib.py │ ├── lambda_.py │ ├── mandel.py │ ├── misc.py │ ├── mypyc_micro.py │ ├── nbody.py │ ├── nt.py │ ├── numbers.py │ ├── p1list.py │ ├── pystone.py │ └── uuid_.py ├── bin │ ├── jpython │ └── jsage ├── dev │ ├── baselib-plain-pretty.js │ ├── compiler.js │ └── signatures.json ├── package-lock.json ├── package.json ├── release │ ├── baselib-plain-pretty.js │ ├── compiler.js │ └── signatures.json ├── src │ ├── ast_types.py │ ├── baselib │ │ ├── builtins.py │ │ ├── containers.py │ │ ├── errors.py │ │ ├── internal.py │ │ ├── itertools.py │ │ └── str.py │ ├── compiler.py │ ├── errors.py │ ├── lib │ │ ├── aes.py │ │ ├── elementmaker.py │ │ ├── encodings.py │ │ ├── gettext.py │ │ ├── js.py │ │ ├── math.py │ │ ├── operator.py │ │ ├── pythonize.py │ │ ├── random.py │ │ ├── re.py │ │ ├── sys.py │ │ ├── time.py │ │ ├── traceback.py │ │ └── uuid.py │ ├── output │ │ ├── __init__.py │ │ ├── classes.py │ │ ├── codegen.py │ │ ├── comments.py │ │ ├── exceptions.py │ │ ├── functions.py │ │ ├── literals.py │ │ ├── loops.py │ │ ├── modules.py │ │ ├── operators.py │ │ ├── statements.py │ │ ├── stream.py │ │ └── utils.py │ ├── parse.py │ ├── string_interpolation.py │ ├── tokenizer.py │ ├── unicode_aliases.py │ └── utils.py ├── test │ ├── _import_one.py │ ├── _import_two │ │ ├── __init__.py │ │ ├── level2 │ │ │ ├── __init__.py │ │ │ └── deep.py │ │ ├── other.py │ │ └── sub.py │ ├── aes_vectors.py │ ├── annotations.py │ ├── baselib.py │ ├── classes.py │ ├── collections_.py │ ├── decorators.py │ ├── docstrings.py │ ├── elementmaker_.py │ ├── functions.py │ ├── generators.py │ ├── generic.py │ ├── imports.py │ ├── jsage.py │ ├── lambda_.py │ ├── lint.py │ ├── loops.py │ ├── newlines.py │ ├── operator_overloading.py │ ├── random_lib.py │ ├── regexp.py │ ├── repl.py │ ├── scoped_flags.py │ ├── starargs.py │ ├── str.py │ └── typing_.py ├── tools │ ├── cli.js │ ├── compile.ts │ ├── compiler.ts │ ├── completer.ts │ ├── embedded_compiler.js │ ├── ini.js │ ├── lint.js │ ├── msgfmt.js │ ├── repl.ts │ ├── self.js │ ├── test.ts │ ├── utils.ts │ └── web_repl.js ├── try.py └── tsconfig.json ├── mpc └── Makefile ├── mpfr └── Makefile ├── ntl ├── Makefile └── TODO.md ├── openssl ├── Makefile └── package.json ├── pari ├── Makefile └── TODO.md ├── python ├── Makefile ├── README.md ├── package.json └── src │ ├── config.site │ ├── python-patches │ ├── 0001-Add-pyodide-callback.patch │ ├── 0001-disable-set_inheritable.patch │ ├── 0002-dont-test-undecodable-filenames.patch │ ├── 0005-add-emscripten-host.patch │ ├── 0006-fix-Py_Sigset_Converter.patch │ ├── 0007-testing.patch │ ├── 0008-setup.patch │ ├── README.md │ ├── ctypes-dont-deref-function-pointer.patch │ ├── remove-duplicate-symbols-from-cfield.c.patch │ └── xfail-core-ctypes-tests-that-don-t-work.patch │ └── xgcd.py ├── readline ├── Makefile ├── Makefile.python └── NOTES.md ├── tsconfig.json ├── wasi ├── Makefile ├── README.md ├── bin │ └── run.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src │ ├── bindings │ │ ├── browser.ts │ │ └── node.ts │ ├── constants.ts │ ├── index.ts │ └── runtime.ts ├── test │ ├── dataview.polyfill.test.ts │ ├── hrtime.bigint.test.ts │ ├── rs │ │ ├── Makefile │ │ ├── args.rs │ │ ├── empty.rs │ │ ├── env.rs │ │ ├── helloworld.rs │ │ ├── sandbox_file_error.rs │ │ ├── sandbox_file_ok.rs │ │ └── stdin.rs │ └── wasi.test.ts └── tsconfig.json ├── wasm-posix ├── Makefile ├── README.md └── src │ ├── wasm-posix.c │ └── wasm-posix.h ├── wasmer └── Makefile ├── zig └── Makefile └── zlib └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | packages/*/build 2 | lib/dist 3 | lib/node_modules 4 | packages/*/dist 5 | packages/*/node_modules 6 | *.term 7 | *.wasm 8 | *.wat 9 | *.pyc 10 | *.o 11 | zig-cache 12 | tsconfig.tsbuildinfo 13 | .hypothesis -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for building jsage in a predictable way. 2 | # This should fully work on both x86_64 and ARM hosts, 3 | # and results in /jsage having everything built 4 | # and the commands jsage and jpython in the PATH. 5 | FROM ubuntu:20.04 6 | 7 | ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 LC_ALL=C.UTF-8 8 | LABEL maintainer="SageMath, Inc. " 9 | 10 | USER root 11 | 12 | # Required apt dependencies -- mainly tools for compiling code. 13 | RUN apt-get update \ 14 | && apt-get install -y git make curl dpkg-dev m4 yasm texinfo python-is-python3 autotools-dev automake libtool vim 15 | 16 | # Required nodejs dependency 17 | RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \ 18 | && apt-get install -y nodejs \ 19 | && npm install -g npm@latest 20 | 21 | # Get source code of JSage and build everything: 22 | ARG commit=HEAD 23 | 24 | RUN git clone https://github.com/sagemathinc/jsage \ 25 | && cd jsage \ 26 | && git checkout ${commit:-HEAD} \ 27 | && make 28 | 29 | RUN echo "export PATH=/jsage/packages/jpython/bin:/jsage/packages/zig/dist/:/jsage/packages/wasmer/dist/bin:$PATH" >> /root/.bashrc 30 | 31 | # Run the test suite, thus increasing the CI value of this Docker image. 32 | RUN cd /jsage/lib \ 33 | && make test 34 | -------------------------------------------------------------------------------- /bin/compile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os, subprocess, sys 3 | 4 | # this is all used at *build* time, so hardcoding the path is fine. 5 | RUN = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', 6 | 'packages', 'wasi', 'bin', 'run.js') 7 | 8 | SCRIPT_DIR = r"""SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )""" 9 | 10 | def run(args): 11 | # print(' '.join(args)) -- don't enable except for debugging, since would obviously break scripts trying to parse output 12 | a = subprocess.run(args, capture_output=True, check=False) 13 | sys.stdout.write(a.stdout.decode('utf-8')) 14 | sys.stderr.write(a.stderr.decode('utf-8')) 15 | if a.returncode: 16 | sys.exit(1) 17 | 18 | 19 | def build(compiler): 20 | args = [ 21 | "zig", 22 | compiler, 23 | "-target", 24 | "wasm32-wasi", 25 | "-D_WASI_EMULATED_SIGNAL", 26 | "-D_WASI_EMULATED_PROCESS_CLOCKS", 27 | ] 28 | prev = None 29 | make_exe = False 30 | wasm_file = '"${BASH_SOURCE[0]}.wasm"' 31 | for x in sys.argv[1:]: 32 | if prev == '-o' and '-c' not in sys.argv: 33 | args.append(x + '.wasm') 34 | wasm_file = x + '.wasm' 35 | make_exe = x 36 | else: 37 | args.append(x) 38 | prev = x 39 | if not make_exe and '-c' not in sys.argv: 40 | # E.g., zcc foo.c produces a.out always. 41 | make_exe = "a.out" 42 | args.append("-o") 43 | args.append("a.out.wasm") 44 | wasm_file = 'a.out.wasm' 45 | run(args) 46 | if make_exe: 47 | file = open(make_exe, 'w') 48 | file.write('#!/usr/bin/env bash\n'+SCRIPT_DIR+'\nnode '+RUN+' "${SCRIPT_DIR}/' + wasm_file + '" "$@"') 49 | file.close() 50 | run(["chmod", "+x", make_exe]) 51 | -------------------------------------------------------------------------------- /bin/z++: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from compile import build 4 | 5 | def main(): 6 | build("c++") 7 | 8 | if __name__ == "__main__": 9 | main() 10 | -------------------------------------------------------------------------------- /bin/zcc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from compile import build 4 | 5 | def main(): 6 | build("cc") 7 | 8 | if __name__ == "__main__": 9 | main() 10 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # JSage 2 | 3 | > "Something like **Sage**, but for the **J**ava**S**cript world." 4 | 5 | See https://github.com/sagemathinc/JSage 6 | 7 | **WARNING:** This is NOT ready for use by anybody yet. 8 | -------------------------------------------------------------------------------- /lib/TODO.md: -------------------------------------------------------------------------------- 1 | Create a zig build system for the zig code in src. It's ugly having toexplicitly specify everywhere. [This discussion](https://www.reddit.com/r/Zig/comments/l5g9ku/the_zig_build_system/) looks helpful. 2 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsage/lib", 3 | "version": "1.11.0", 4 | "description": "SageJS", 5 | "exports": { 6 | ".": "./dist/index.js", 7 | "./*": "./dist/*.js", 8 | "./python": "./dist/python/index.js", 9 | "./pari": "./dist/pari/index.js", 10 | "./modular": "./dist/modular/index.js", 11 | "./integer": "./dist/integer/index.js" 12 | }, 13 | "files": [ 14 | "dist/*", 15 | "README.md", 16 | "package.json", 17 | "tsconfig.json", 18 | "Makefile" 19 | ], 20 | "scripts": { 21 | "build": "make all", 22 | "clean": "make clean", 23 | "tsc": "npx tsc -w", 24 | "test": "jest ./dist", 25 | "test-watch": "jest --watch ./dist" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/sagemathinc/jsage.git" 30 | }, 31 | "keywords": [ 32 | "number", 33 | "theory" 34 | ], 35 | "author": "William Stein", 36 | "license": "LGPL-3.0-or-later", 37 | "bugs": { 38 | "url": "https://github.com/sagemathinc/JSage/issues" 39 | }, 40 | "homepage": "https://github.com/sagemathinc/JSage/tree/main/lib", 41 | "workspaces": [ 42 | "../packages/wasi" 43 | ], 44 | "dependencies": { 45 | "@jsage/wasi": "^0.14.0", 46 | "async-await-utils": "^3.0.1", 47 | "callable-instance": "^2.0.0", 48 | "callsite": "^1.0.0", 49 | "timit": "^1.0.0" 50 | }, 51 | "devDependencies": { 52 | "@types/callsite": "^1.0.31", 53 | "@types/jest": "^27.0.2", 54 | "@types/node": "^16.7.12", 55 | "jest": "^27.2.1", 56 | "typescript": "^4.4.2" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/arith-export.zig: -------------------------------------------------------------------------------- 1 | const arith = @import("./arith.zig"); 2 | 3 | pub export fn gcd(a: i32, b: i32) i32 { 4 | return arith.gcd(a, b); 5 | } 6 | 7 | // returns -1 on error 8 | pub export fn inverseMod(a: i32, N: i32) i32 { 9 | return arith.inverseMod(a, N) catch { 10 | return -1; 11 | }; 12 | } 13 | 14 | extern fn xgcd_cb(g: i32, s: i32, t: i32) void; 15 | pub export fn xgcd(a: i32, b: i32) void { 16 | const z = arith.xgcd(a, b); 17 | xgcd_cb(z.g, z.s, z.t); 18 | } 19 | 20 | pub export fn bench_gcd1(k: i32, B: i32) i32 { 21 | var s: i32 = 0; 22 | var n: i32 = 0; 23 | while (n <= B) : (n += 1) { 24 | s += gcd(n, k); 25 | } 26 | return s; 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/arith.ts: -------------------------------------------------------------------------------- 1 | import wasmImport from "./wasm"; 2 | 3 | export function gcd_js(a, b) { 4 | if (a == 0) { 5 | return Math.abs(b); 6 | } 7 | if (b == 0) { 8 | return Math.abs(a); 9 | } 10 | if (a < 0) { 11 | a = -a; 12 | } 13 | if (b < 0) { 14 | b = -b; 15 | } 16 | while (b != 0) { 17 | const c = a % b; 18 | a = b; 19 | b = c; 20 | } 21 | return a; 22 | } 23 | 24 | const MAX_i32 = 2 ** (31 - 1); 25 | const MIN_i32 = -(2 ** 31); 26 | 27 | function checkConvertsToi32(n: number) { 28 | if (n < MIN_i32 || n > MAX_i32) { 29 | throw Error(`${n} cannot be represented as a 32-bit int`); 30 | } 31 | } 32 | export function gcd(n: number, m: number): number { 33 | checkConvertsToi32(n); 34 | checkConvertsToi32(m); 35 | return wasm.exports.gcd(n, m); 36 | } 37 | 38 | export function inverseMod(a: number, N: number): number { 39 | checkConvertsToi32(a); 40 | checkConvertsToi32(N); 41 | const b = wasm.exports.inverseMod(a, N); 42 | if (b == -1) { 43 | throw Error(`Mod(${a}, ${N}) is not invertible`); 44 | } 45 | return b; 46 | } 47 | 48 | let xgcd_r: [number, number, number] = [0, 0, 0]; 49 | const xgcd_cb = (g, s, t) => { 50 | xgcd_r = [g, s, t]; 51 | }; 52 | export function xgcd(a: number, b: number): [number, number, number] { 53 | checkConvertsToi32(a); 54 | checkConvertsToi32(b); 55 | wasm.exports.xgcd(a, b); 56 | return xgcd_r; 57 | } 58 | 59 | let wasm: any = undefined; 60 | export async function init() { 61 | wasm = await wasmImport("arith", { env: { xgcd_cb }, noWasi: true }); 62 | } 63 | 64 | export function bench_gcd1(k, B) { 65 | let s = 0; 66 | for (let n = 0; n <= B; n++) { 67 | s += wasm.exports.gcd(n, k); 68 | } 69 | return s; 70 | } 71 | 72 | export function bench_gcd1_js(k, B) { 73 | let s = 0; 74 | for (let n = 0; n <= B; n++) { 75 | s += gcd_js(n, k); 76 | } 77 | return s; 78 | } 79 | 80 | export function bench_gcd1_direct(k, B) { 81 | return wasm.exports.bench_gcd1(k, B); 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/arith.zig: -------------------------------------------------------------------------------- 1 | const errors = @import("errors.zig"); 2 | 3 | pub fn mod(a: anytype, N: anytype) @TypeOf(a) { 4 | if (N == 0) return 0; 5 | var b = @mod(a, abs(N)); 6 | if (b < 0) { 7 | b += N; 8 | } 9 | return b; 10 | } 11 | 12 | pub fn abs(a: anytype) @TypeOf(a) { 13 | return if (a < 0) -a else a; 14 | } 15 | 16 | pub fn sign(a: anytype) @TypeOf(a) { 17 | return if (a < 0) -1 else 1; 18 | } 19 | 20 | pub fn gcd(a: anytype, b: anytype) @TypeOf(a) { 21 | if (a == 0) { 22 | return abs(b); 23 | } 24 | if (b == 0) { 25 | return abs(a); 26 | } 27 | var x = a; 28 | var y = b; 29 | if (x < 0) { 30 | x = -x; 31 | } 32 | if (y < 0) { 33 | y = -y; 34 | } 35 | while (y != 0) { 36 | const c = @mod(x, y); 37 | x = y; 38 | y = c; 39 | } 40 | return x; 41 | } 42 | 43 | pub fn xgcd(a0: anytype, b0: anytype) struct { g: @TypeOf(a0), s: @TypeOf(a0), t: @TypeOf(a0) } { 44 | if (a0 == 0) { 45 | return .{ .g = abs(b0), .s = 0, .t = sign(b0) }; 46 | } 47 | if (b0 == 0) { 48 | return .{ .g = abs(a0), .s = sign(a0), .t = 0 }; 49 | } 50 | const T = @TypeOf(a0); 51 | var psign: T = 1; 52 | var qsign: T = 1; 53 | var a: T = a0; 54 | var b: T = b0; 55 | if (a < 0) { 56 | a = -a; 57 | psign = -1; 58 | } 59 | if (b < 0) { 60 | b = -b; 61 | qsign = -1; 62 | } 63 | var p: T = 1; 64 | var q: T = 0; 65 | var r: T = 0; 66 | var s: T = 1; 67 | while (b != 0) { 68 | const c = mod(a, b); 69 | const quot = @divFloor(a, b); 70 | a = b; 71 | b = c; 72 | const new_r = p - quot * r; 73 | const new_s = q - quot * s; 74 | p = r; 75 | q = s; 76 | r = new_r; 77 | s = new_s; 78 | } 79 | 80 | return .{ .g = a, .s = p * psign, .t = q * qsign }; 81 | } 82 | 83 | // compute multiplicative inverse of a modulo N. 84 | pub fn inverseMod(a: anytype, N: anytype) !@TypeOf(a) { 85 | if (a == 1 or N <= 1) { 86 | // common special cases 87 | return mod(a, N); 88 | } 89 | const xgcd_aN = xgcd(a, N); 90 | if (xgcd_aN.g != 1) { 91 | return errors.Math.ZeroDivisionError; 92 | } 93 | return mod(xgcd_aN.s, N); 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/arith/generic-power.ts: -------------------------------------------------------------------------------- 1 | interface MonoidElement { 2 | __mul__: Function; 3 | } 4 | 5 | export default function genericPower(a: MonoidElement, n: number) { 6 | if (n <= 0) { 7 | throw Error("n must be positive"); 8 | } 9 | // Find least significant set bit as starting point 10 | let apow = a; 11 | let m = n; 12 | while ((m & 1) == 0) { 13 | apow = apow.__mul__(apow); 14 | m >>= 1; 15 | } 16 | 17 | // Now multiply together the correct factors a^(2^i) 18 | let res = apow; 19 | m >>= 1; 20 | while (m != 0) { 21 | apow = apow.__mul__(apow); 22 | if ((m & 1) != 0) { 23 | res = apow.__mul__(res); 24 | } 25 | m >>= 1; 26 | } 27 | 28 | return res; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/arith/generic-power.zig: -------------------------------------------------------------------------------- 1 | // See sage/src/sage/arith/power.pyx 2 | 3 | const std = @import("std"); 4 | const errors = @import("../errors.zig"); 5 | 6 | pub fn genericPower(a: anytype, n: usize) !@TypeOf(a) { 7 | if (n == 0) { 8 | return errors.Math.ValueError; 9 | } 10 | // Find least significant set bit as starting point 11 | var apow = a; 12 | var m = n; 13 | while ((m & 1) == 0) : (m >>= 1) { 14 | apow = apow.mul(apow); 15 | } 16 | 17 | // Now multiply together the correct factors a^(2^i) 18 | var res = apow; 19 | m >>= 1; 20 | while (m != 0) : (m >>= 1) { 21 | apow = apow.mul(apow); 22 | if (m & 1 != 0) { 23 | res = apow.mul(res); 24 | } 25 | } 26 | 27 | return res; 28 | } 29 | 30 | // This doesn't work properly because it doesn't 31 | // call deinit along the way. 32 | pub fn genericPowerAlloc(a: anytype, n: usize) !@TypeOf(a) { 33 | if (n == 0) { 34 | return errors.Math.ValueError; 35 | } 36 | // Find least significant set bit as starting point 37 | var apow = a; 38 | var m = n; 39 | while ((m & 1) == 0) : (m >>= 1) { 40 | apow = try apow.mul(apow); 41 | } 42 | 43 | // Now multiply together the correct factors a^(2^i) 44 | var res = apow; 45 | m >>= 1; 46 | while (m != 0) : (m >>= 1) { 47 | apow = try apow.mul(apow); 48 | if (m & 1 != 0) { 49 | res = try apow.mul(res); 50 | } 51 | } 52 | 53 | return res; 54 | } 55 | 56 | const expect = std.testing.expect; 57 | 58 | const Number = struct { 59 | x: i32, 60 | pub fn mul(a: Number, b: Number) Number { 61 | return Number{ .x = a.x * b.x }; 62 | } 63 | }; 64 | 65 | test "compute some powers" { 66 | const b: i32 = 12; 67 | var a = Number{ .x = b }; 68 | try expect((try genericPower(a, 1)).x == b); 69 | try expect((try genericPower(a, 2)).x == b * b); 70 | try expect((try genericPower(a, 7)).x == b * b * b * b * b * b * b); 71 | } 72 | -------------------------------------------------------------------------------- /lib/src/bench/fib/fib.jl: -------------------------------------------------------------------------------- 1 | # include("fib.jl") 2 | # @time bench(35) 3 | 4 | function fib(n::Int) 5 | n<0 && error("n must be non negative") 6 | n<=1 && return 1 7 | fib(n-1) + fib(n-2) 8 | end 9 | 10 | function bench(n::Int) 11 | k = fib(n) 12 | println("fib", n, "=", k) 13 | end -------------------------------------------------------------------------------- /lib/src/bench/fib/fib.js: -------------------------------------------------------------------------------- 1 | function fib(n) { 2 | if (n <= 1) { 3 | return 1; 4 | } 5 | return fib(n - 1) + fib(n - 2); 6 | } 7 | 8 | exports.bench = function bench(n = 35) { 9 | t = new Date(); 10 | console.log(fib(n), `${new Date() - t} ms`); 11 | }; 12 | -------------------------------------------------------------------------------- /lib/src/bench/fib/fib.py: -------------------------------------------------------------------------------- 1 | def fib(n): 2 | if n == 1 or n == 0: 3 | return 1 4 | return fib(n - 1) + fib(n - 2) 5 | 6 | 7 | def bench(n=35): 8 | from time import time 9 | t = time() 10 | print(fib(n), f"{(time() - t) * 1000} ms") 11 | -------------------------------------------------------------------------------- /lib/src/bench/fib/fib.rb: -------------------------------------------------------------------------------- 1 | def fib n 2 | if n <= 1 3 | 1 4 | else 5 | fib(n - 1) + fib(n - 2) 6 | end 7 | end 8 | 9 | 10 | print(fib 35) 11 | -------------------------------------------------------------------------------- /lib/src/bench/fib/fib.zig: -------------------------------------------------------------------------------- 1 | // zig test fib.zig -O ReleaseFast 2 | 3 | const std = @import("std"); 4 | const time = std.time.milliTimestamp; 5 | 6 | fn fib(comptime T: type, n: i32) T { 7 | if (n <= 1) { 8 | return @as(T, 1); 9 | } 10 | return fib(T, n - 1) + fib(T, n - 2); 11 | } 12 | 13 | fn bench(comptime T: type, n: anytype) void { 14 | var t = time(); 15 | std.debug.print("\nfib({}) = {}, {}ms\n", .{ n, fib(T, n), (time() - t) }); 16 | } 17 | 18 | test "try it on some values of n using i32" { 19 | bench(i32, 10); 20 | bench(i32, 20); 21 | bench(i32, 30); 22 | bench(i32, 35); 23 | bench(i32, 40); 24 | } 25 | 26 | test "try it on some values of n using i16" { 27 | bench(i16, 10); 28 | bench(i16, 20); 29 | } 30 | 31 | test "try it on some values of n using i64" { 32 | bench(i64, 30); 33 | bench(i64, 35); 34 | bench(i64, 40); 35 | } 36 | 37 | test "try up to 45" { 38 | var i : i32 = 1; 39 | while (i<=45) : (i+=1) { 40 | bench(i32, i); 41 | } 42 | } -------------------------------------------------------------------------------- /lib/src/complex/complex.ts: -------------------------------------------------------------------------------- 1 | import wasmImport from "../wasm"; 2 | 3 | class ComplexNumberClass { 4 | private re: number; 5 | private im: number; 6 | 7 | constructor(re: number, im: number) { 8 | this.re = re; 9 | this.im = im; 10 | } 11 | 12 | __mul__(other: ComplexNumberClass): ComplexNumberClass { 13 | const re = this.re * other.re - this.im * other.im; 14 | const im = this.im * other.re + this.re * other.im; 15 | return new ComplexNumberClass(re, im); 16 | } 17 | /* 18 | __mul2__(other: ComplexNumberClass): ComplexNumberClass { 19 | wasm.exports.mul(this.re, this.im, other.re, other.im); 20 | return result; 21 | }*/ 22 | 23 | __add__(other: ComplexNumberClass): ComplexNumberClass { 24 | return new ComplexNumberClass(this.re + other.re, this.im + other.im); 25 | } 26 | __sub__(other: ComplexNumberClass): ComplexNumberClass { 27 | return new ComplexNumberClass(this.re - other.re, this.im - other.im); 28 | } 29 | __div__(other: ComplexNumberClass): ComplexNumberClass { 30 | const re_num = this.re * other.re + this.im * other.im; 31 | const im_num = this.im * other.re - this.re * other.im; 32 | const den = other.re * other.re + other.im * other.im; 33 | 34 | return new ComplexNumberClass(re_num / den, im_num / den); 35 | } 36 | __abs__(): number { 37 | return Math.sqrt(this.re * this.re + this.im * this.im); 38 | } 39 | __repr__() { 40 | if (!this.im) return this.re.toString(); 41 | if (!this.re) return `${this.im}*I`; 42 | return `${this.re} + ${this.im}*I`; 43 | } 44 | exp() { 45 | wasm.exports.exp(this.re, this.im); 46 | return result; 47 | } 48 | } 49 | 50 | export function ComplexNumber(re: number, im: number): ComplexNumberClass { 51 | return new ComplexNumberClass(re, im); 52 | } 53 | 54 | /* 55 | 56 | Using WASM/Zig turns out to be about 20x slower for **multiplication** since 57 | Javascript is so good at JIT for this and there is extra overhead moving two 58 | floats back via a function call (maybe pointers would be better?). For functions 59 | this approach is probably good and at least saves implementation time. 60 | 61 | */ 62 | 63 | let result: ComplexNumberClass = new ComplexNumberClass(0, 0); 64 | function sendComplex(re: number, im: number) { 65 | result = new ComplexNumberClass(re, im); 66 | } 67 | 68 | export let wasm: any = undefined; 69 | export async function init() { 70 | wasm = await wasmImport("complex/complex", { 71 | env: { sendComplex }, 72 | noWasi: true, 73 | }); 74 | } 75 | init(); 76 | -------------------------------------------------------------------------------- /lib/src/complex/complex.zig: -------------------------------------------------------------------------------- 1 | // Zig has a nice implementation of complex numbers from scratch for different 2 | // bit sizes in their standard library: lib/std/math/complex.zig 3 | // Here we make them available to Javascript via WASM. 4 | // The obvious *hope* is that this is more efficient than an implementation 5 | // directly in Javascript -- for special functions that's clear, but 6 | // for basic arithmetic it is not. 7 | 8 | const std = @import("std"); 9 | const Complex = std.math.complex.Complex; 10 | 11 | extern fn sendComplex(re: f64, im: f64) void; 12 | 13 | export fn mul(re0: f64, im0: f64, re1: f64, im1: f64) void { 14 | const left = Complex(f64).init(re0, im0); 15 | const right = Complex(f64).init(re1, im1); 16 | const ans = left.mul(right); 17 | sendComplex(ans.re, ans.im); 18 | } 19 | 20 | export fn exp(re: f64, im: f64) void { 21 | const x = Complex(f64).init(re, im); 22 | const y = std.math.complex.exp(x); 23 | sendComplex(y.re, y.im); 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/custom-allocator.zig: -------------------------------------------------------------------------------- 1 | // see https://gmplib.org/manual/Custom-Allocation 2 | 3 | const std = @import("std"); 4 | const allocator = @import("./interface/allocator.zig"); 5 | const gmp = @cImport(@cInclude("gmp.h")); 6 | 7 | var initialized = false; 8 | pub fn init() void { 9 | if (initialized) return; 10 | //std.debug.print("initializing custom GMP allocator...\n", .{}); 11 | initialized = true; 12 | gmp.mp_set_memory_functions(gmp_alloc, gmp_realloc, gmp_free); 13 | } 14 | 15 | //const stdout = std.io.getStdOut().writer(); 16 | 17 | var allocError = false; 18 | pub fn checkError() bool { 19 | if (allocError) { 20 | allocError = false; 21 | return true; 22 | } 23 | return false; 24 | } 25 | 26 | export fn gmp_alloc(n: usize) ?*anyopaque { 27 | // std.debug.print("allocating n = {} bytes\n", .{n}); 28 | const t = allocator.get().alloc(u8, n) catch |err| 29 | { 30 | std.debug.print("failed to alloc -- {}", .{err}); 31 | allocError = true; 32 | return null; 33 | }; 34 | //std.debug.print("successfully got memory t={*}\n", .{t}); 35 | return t.ptr; 36 | } 37 | 38 | export fn gmp_free(ptr: ?*anyopaque, n: usize) void { 39 | //std.debug.print("free ptr={*} and {} bytes\n", .{ ptr, n }); 40 | // This is how to turn void* from c into a **slice** of memory that 41 | // knows about its length! Yes, this took me a long time to figure out. 42 | const p = @ptrCast([*]u8, ptr)[0..n]; 43 | allocator.get().free(p); 44 | } 45 | 46 | pub fn free(slice: []u8) void { 47 | if (initialized) { 48 | allocator.get().free(slice); 49 | } else { 50 | // still using malloc 51 | std.c.free(slice.ptr); 52 | } 53 | } 54 | 55 | export fn gmp_realloc(ptr: ?*anyopaque, n: usize, m: usize) ?*anyopaque { 56 | //std.debug.print("realloc n={},m={} bytes at ptr={*}\n", .{ n, m, ptr }); 57 | const p = @ptrCast([*]u8, ptr)[0..n]; 58 | const t = allocator.get().realloc(p, m) catch |err| 59 | { 60 | std.debug.print("failed to realloc -- {}", .{err}); 61 | unreachable; 62 | }; 63 | //std.debug.print("successfully got realloc t={*}\n", .{t}); 64 | return t.ptr; 65 | } 66 | -------------------------------------------------------------------------------- /lib/src/errors.zig: -------------------------------------------------------------------------------- 1 | pub const Math = error{ ValueError, ZeroDivisionError, OverflowError }; 2 | 3 | pub const General = error{ IndexError, StopIteration, RuntimeError, NotImplementedError, MemoryError, TypeError}; 4 | -------------------------------------------------------------------------------- /lib/src/factor-export.zig: -------------------------------------------------------------------------------- 1 | const factor = @import("factor.zig"); 2 | const std = @import("std"); 3 | 4 | extern fn sendPrimePower(p: i32, e: i32) void; 5 | extern fn reportError() void; 6 | 7 | // The static version below is fine up to 128 bits 8 | // and probably much faster than dynamic allocation. 9 | 10 | // const allocator = @import("./interface/allocator.zig"); 11 | // pub export fn factorTrialDivision(N: i32) void { 12 | // const F = factor.factorTrialDivision(allocator.get(), N) catch { 13 | // reportError(); 14 | // return; 15 | // }; 16 | // defer F.deinit(); 17 | // for (F.items) |primePower| { 18 | // sendPrimePower(primePower.p, primePower.e); 19 | // } 20 | // } 21 | 22 | pub export fn factorTrialDivision(N: i32) void { 23 | const F = factor.smallFactor(N); 24 | var i: u8 = 0; 25 | while (i < F.len) : (i += 1) { 26 | sendPrimePower(F.factors[i].p, F.factors[i].e); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/factor.ts: -------------------------------------------------------------------------------- 1 | import wasmImport from "./wasm"; 2 | 3 | let wasm: any = undefined; 4 | export async function init() { 5 | wasm = await wasmImport("factor", { 6 | env: { 7 | sendPrimePower, 8 | reportError: () => { 9 | throw Error("error"); 10 | }, 11 | }, 12 | noWasi: true, 13 | }); 14 | } 15 | init(); 16 | 17 | interface PrimePower { 18 | p: number; 19 | e: number; 20 | } 21 | 22 | let v: PrimePower[] = []; 23 | function sendPrimePower(p, e) { 24 | v.push({ p, e }); 25 | } 26 | 27 | export function factorTrialDivision(N: number) { 28 | if (N < 1 || N > 2147483647) { 29 | throw Error("N must be a positive 32-bit signed int"); 30 | } 31 | v = []; 32 | wasm.exports.factorTrialDivision(N); 33 | return v; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/flint/interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn helloWorld() void { 4 | std.debug.print("hello\n"); 5 | } -------------------------------------------------------------------------------- /lib/src/index.ts: -------------------------------------------------------------------------------- 1 | // Import and initialize everything. This is convenient for interactive use. 2 | 3 | export { ComplexNumber } from "./complex/complex"; 4 | import * as dims from "./modular/dims"; 5 | export { dims }; 6 | import P1List, { init as p1listInit } from "./modular/p1list"; 7 | export { P1List }; 8 | 9 | import ManinSymbols, { 10 | init as maninSymbolsInit, 11 | } from "./modular/manin-symbols"; 12 | export { ManinSymbols }; 13 | 14 | import { 15 | EllipticCurve, 16 | init as EllipticCurveInit, 17 | } from "./modular/elliptic-curve"; 18 | export { EllipticCurve }; 19 | 20 | import * as factor from "./factor"; 21 | export { factor }; 22 | import * as arith from "./arith"; 23 | export { arith }; 24 | import { Integer, init as integerInit, ZZ } from "./integer"; 25 | export { Integer, ZZ }; 26 | import { Rational, init as rationalInit, QQ } from "./rational"; 27 | export { Rational, QQ }; 28 | import * as pari from "./pari"; 29 | export { pari }; 30 | export * as misc from "./misc"; 31 | 32 | export { Number } from "./number"; 33 | 34 | export async function init(): Promise { 35 | await Promise.all([ 36 | dims.init(), 37 | p1listInit(), 38 | maninSymbolsInit(), 39 | EllipticCurveInit(), 40 | factor.init(), 41 | arith.init(), 42 | integerInit(), 43 | rationalInit(), 44 | /*pari.init(),*/ 45 | ]); 46 | } 47 | init(); 48 | -------------------------------------------------------------------------------- /lib/src/integer/index.ts: -------------------------------------------------------------------------------- 1 | import Integer, { init } from "./integer"; 2 | export { Integer, init }; 3 | import ZZ from "./integer-ring"; 4 | export { ZZ }; 5 | -------------------------------------------------------------------------------- /lib/src/integer/integer-ring.ts: -------------------------------------------------------------------------------- 1 | // The ring ZZ. The main point of this for now is to show how 2 | // to make ZZ('290328028340823904') work in pure Javascript. 3 | 4 | import CallableInstance from "callable-instance"; // this is just a few lines of code 5 | import { IntegerClass } from "./integer"; 6 | 7 | class IntegerRing extends CallableInstance<[number], string> { 8 | constructor() { 9 | super("element"); 10 | } 11 | 12 | __str__() { 13 | return "Integer Ring"; 14 | } 15 | 16 | element(n: number | string, base: number = 10): IntegerClass { 17 | return new IntegerClass(n, base); 18 | } 19 | } 20 | 21 | const ZZ = new IntegerRing(); 22 | 23 | export default ZZ; 24 | -------------------------------------------------------------------------------- /lib/src/integer/integer.test.ts: -------------------------------------------------------------------------------- 1 | import Integer, { init } from "./integer"; 2 | import ZZ from "./integer-ring"; 3 | 4 | beforeEach(async () => { 5 | await init(); 6 | }); 7 | 8 | test("check some primality", async () => { 9 | expect(Integer(101).isPseudoPrime()).toBe(2); 10 | expect(Integer("101").isPseudoPrime()).toBe(2); 11 | expect(Integer(2021).isPseudoPrime()).toBe(0); 12 | expect(Integer("2021").isPseudoPrime()).toBe(0); 13 | }); 14 | 15 | test("Create integer object and do basic arithmetic", async () => { 16 | const n = Integer(15); 17 | const m = Integer(20); 18 | // toString not really implemented yet 19 | //expect(n.add(m).toString()).toBe("35"); 20 | //expect(n.mul(m).toString()).toBe("300"); 21 | expect(n.__add__(m).eql(Integer(35))).toBe(true); 22 | expect(n.__mul__(m).eql(Integer(300))).toBe(true); 23 | expect(n.__sub__(m).eql(Integer(-5))).toBe(true); 24 | expect(n.__pow__(20).eql(Integer("332525673007965087890625"))).toBe(true); 25 | }); 26 | 27 | test("Find the next prime year", async () => { 28 | const n = Integer(2021); 29 | const m = n.nextPrime(); 30 | expect(m.eql(Integer(2027))).toBe(true); 31 | }); 32 | 33 | test("converting integer to string", async () => { 34 | const n = Integer(-17); 35 | expect(n.__repr__() == "-17"); 36 | expect(JSON.stringify(n) == '{"type":"Integer","hex":"-11"}'); 37 | }); 38 | 39 | test("converting integer to string when bound is not sharp", async () => { 40 | const n = Integer(567); 41 | expect(n.ndigitsBound() == 4); 42 | const s = n.__repr__(); 43 | expect(s == "567"); 44 | expect(JSON.stringify(n) == '{"type":"Integer","hex":"237"}'); 45 | }); 46 | 47 | test("using the integer ring", async () => { 48 | expect(ZZ.element(-17).__repr__() == "-17"); 49 | expect(ZZ.element("-11", 16).__repr__() == "-17"); 50 | }); 51 | -------------------------------------------------------------------------------- /lib/src/interface.zig: -------------------------------------------------------------------------------- 1 | pub const ProxyObjects = @import("./interface/proxy.zig").ProxyObjects; 2 | pub const throw = @import("./interface/util.zig").throw; 3 | pub const allocator = @import("./interface/allocator.zig").get; 4 | -------------------------------------------------------------------------------- /lib/src/interface/allocator.zig: -------------------------------------------------------------------------------- 1 | // This is the one allocator we use for the WebAssembly interface. 2 | 3 | const std = @import("std"); 4 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 5 | 6 | pub fn get() std.mem.Allocator { 7 | return gpa.allocator(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/interface/gmp.zig: -------------------------------------------------------------------------------- 1 | const custom = @import("../custom-allocator.zig"); 2 | const rational = @import("../rational/interface.zig"); 3 | const integer = @import("../integer/interface.zig"); 4 | 5 | // Change to using Zig instead of malloc for GMP memory. 6 | pub export fn initCustomAllocator() void { 7 | //std.debug.print("GMP: initCustomAllocator\n", .{}); 8 | custom.init(); 9 | } 10 | 11 | // this is a no-op to prevent rational and integer from getting optimized away 12 | pub export fn init() void { 13 | integer.init(); 14 | rational.init(); 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/interface/util.zig: -------------------------------------------------------------------------------- 1 | extern fn reportError(ptr: [*]const u8, len: usize) void; 2 | 3 | pub fn throw(s: [*]const u8) void { 4 | var i: usize = 0; 5 | while (s[i] != 0) : (i += 1) {} 6 | reportError(s, i); 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/src/misc/index.ts: -------------------------------------------------------------------------------- 1 | import prod from "./prod"; 2 | export { prod }; 3 | -------------------------------------------------------------------------------- /lib/src/misc/prod.ts: -------------------------------------------------------------------------------- 1 | // see sage/src/sage/misc/misc_c.pyx 2 | 3 | function mul(a, b) { 4 | if (a.__mul__ != null) { 5 | return a.__mul__(b); 6 | } 7 | return a * b; 8 | } 9 | 10 | export default function prod(v: any[], z = undefined, recursionCutoff = 5) { 11 | if (!Array.isArray(v)) { 12 | throw Error("v must be an array"); 13 | } 14 | const n = v.length; 15 | 16 | if (n == 0) { 17 | return z === undefined ? 1 : z; 18 | } 19 | 20 | let pr = balancedListProd(v, 0, n, recursionCutoff); 21 | if (z !== undefined) { 22 | pr = mul(pr, z); 23 | } 24 | return pr; 25 | } 26 | 27 | function balancedListProd( 28 | v: any[], 29 | offset: number, 30 | count: number, 31 | cutoff: number 32 | ) { 33 | if (count <= cutoff) { 34 | let pr = v[offset]; 35 | for (let k = offset + 1; k < offset + count; k++) { 36 | pr = mul(pr, v[k]); 37 | } 38 | return pr; 39 | } else { 40 | const k = (1 + count) >> 1; 41 | return mul( 42 | balancedListProd(v, offset, k, cutoff), 43 | balancedListProd(v, offset + k, count - k, cutoff) 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/src/modular/dense-matrix.ts: -------------------------------------------------------------------------------- 1 | import { DenseVector } from "./dense-vector"; 2 | import { init as pariInit, exec as pariExec } from "../pari"; 3 | import wasmImport from "../wasm"; 4 | export let wasm: any = undefined; 5 | export async function init() { 6 | wasm = await wasmImport("modular/manin-symbols"); 7 | } 8 | init(); 9 | 10 | // @ts-ignore 11 | const registry = new FinalizationRegistry((handle) => { 12 | wasm.exports.DenseMatrix_free(handle); 13 | }); 14 | 15 | export class DenseMatrix { 16 | private readonly handle: number; 17 | 18 | constructor(handle: number) { 19 | if (handle <= 0) { 20 | throw Error(`invalid handle ${handle}`); 21 | } 22 | this.handle = handle; 23 | registry.register(this, this.handle); 24 | } 25 | 26 | __repr__(): string { 27 | wasm.exports.DenseMatrix_format(this.handle); 28 | return wasm.result; 29 | } 30 | 31 | toJSON(): { 32 | type: "DenseMatrixMod"; 33 | modulus: number; 34 | nrows: number; 35 | ncols: number; 36 | entries: number[]; 37 | } { 38 | wasm.exports.DenseMatrix_stringify(this.handle); 39 | return JSON.parse(wasm.result); 40 | } 41 | 42 | getRow(i: number): DenseVector { 43 | return new DenseVector(wasm.exports.DenseMatrix_getRow(this.handle, i)); 44 | } 45 | 46 | modulus(): number { 47 | return wasm.exports.DenseMatrix_modulus(this.handle); 48 | } 49 | 50 | kernel(): DenseMatrix { 51 | return new DenseMatrix(wasm.exports.DenseMatrix_kernel(this.handle)); 52 | } 53 | 54 | __sub__(other) { 55 | return this.subtractScalar(other); 56 | } 57 | 58 | subtractScalar(scalar: number): DenseMatrix { 59 | return new DenseMatrix( 60 | wasm.exports.DenseMatrix_subtractScalar(this.handle, scalar) 61 | ); 62 | } 63 | 64 | transpose(): DenseMatrix { 65 | return new DenseMatrix(wasm.exports.DenseMatrix_transpose(this.handle)); 66 | } 67 | 68 | nrows(): number { 69 | return wasm.exports.DenseMatrix_nrows(this.handle); 70 | } 71 | 72 | ncols(): number { 73 | return wasm.exports.DenseMatrix_ncols(this.handle); 74 | } 75 | 76 | rank(): number { 77 | return wasm.exports.DenseMatrix_rank(this.handle); 78 | } 79 | 80 | toPariString(): string { 81 | const { modulus, nrows, ncols, entries } = this.toJSON(); 82 | // make it in pari 83 | let s: string = "["; 84 | for (let i = 0; i < nrows; i++) { 85 | if (i > 0) s += ";"; 86 | for (let j = 0; j < ncols; j++) { 87 | s += `${j > 0 ? "," : ""}Mod(${entries[i * ncols + j]},${modulus})`; 88 | } 89 | } 90 | s += "]"; 91 | return s; 92 | } 93 | 94 | // quick and dirty factored charpoly for now that just prints it. 95 | async fcp(): Promise { 96 | // NOTE: this will just fail the first time due to needing to 97 | // init pari... 98 | await pariInit(); 99 | console.log(pariExec(`lift(factor(charpoly(${this.toPariString()})))`)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/src/modular/dense-vector-interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const interface = @import("../interface.zig"); 3 | const dense_vector = @import("./dense-vector.zig"); 4 | const errors = @import("../errors.zig"); 5 | 6 | const DenseVectorType = dense_vector.DenseVectorMod(i32); 7 | var DenseVector_objects = interface.ProxyObjects(DenseVectorType).init(); 8 | 9 | pub export fn DenseVector_free(handle: i32) void { 10 | DenseVector_objects.free(handle); 11 | } 12 | 13 | pub fn DenseVector_get(handle: i32) !DenseVectorType { 14 | return DenseVector_objects.get(handle) orelse { 15 | interface.throw("DenseVector: failed to get DenseVector with given handle"); 16 | return errors.General.RuntimeError; 17 | }; 18 | } 19 | 20 | pub fn DenseVector_put(v: DenseVectorType) i32 { 21 | return DenseVector_objects.put(v) catch { 22 | interface.throw("DenseVector: failed to store"); 23 | return 0; 24 | }; 25 | } 26 | 27 | pub export fn DenseVector_stringify(handle: i32) void { 28 | DenseVector_objects.stringify(handle); 29 | } 30 | 31 | pub export fn DenseVector_format(handle: i32) void { 32 | DenseVector_objects.format(handle); 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/modular/dense-vector.ts: -------------------------------------------------------------------------------- 1 | import wasmImport from "../wasm"; 2 | export let wasm: any = undefined; 3 | export async function init() { 4 | wasm = await wasmImport("modular/manin-symbols"); 5 | } 6 | init(); 7 | 8 | // @ts-ignore 9 | const registry = new FinalizationRegistry((handle) => { 10 | wasm.exports.DenseVector_free(handle); 11 | }); 12 | 13 | export class DenseVector { 14 | private readonly handle: number; 15 | 16 | constructor(handle: number) { 17 | this.handle = handle; 18 | registry.register(this, this.handle); 19 | } 20 | 21 | __repr__(): string { 22 | wasm.exports.DenseVector_format(this.handle); 23 | return wasm.result; 24 | } 25 | 26 | toJSON(): object { 27 | wasm.exports.DenseVector_stringify(this.handle); 28 | return JSON.parse(wasm.result); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/modular/dims.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | dimensionCuspForms, 3 | dimensionEisensteinSeries, 4 | dimensionModularForms, 5 | init, 6 | } from "./dims"; 7 | 8 | beforeEach(async () => { 9 | await init(); 10 | }); 11 | 12 | test("sum of dimensions of cusp form spaces up to level 10000", () => { 13 | const B = 10000; 14 | let s = 0; 15 | for (let N = 1; N <= B; N++) { 16 | s += dimensionCuspForms(N); 17 | } 18 | expect(s).toBe(6268440); 19 | }); 20 | 21 | test("sum of dimensions of modular form spaces up to level 10000", () => { 22 | const B = 10000; 23 | let s = 0; 24 | for (let N = 1; N <= B; N++) { 25 | s += dimensionModularForms(N); 26 | } 27 | expect(s).toBe(6402996); 28 | }); 29 | 30 | test("sum of dimensions of eisenstein series spaces up to level 10000", () => { 31 | const B = 10000; 32 | let s = 0; 33 | for (let N = 1; N <= B; N++) { 34 | s += dimensionEisensteinSeries(N); 35 | } 36 | expect(s).toBe(134556); 37 | }); 38 | -------------------------------------------------------------------------------- /lib/src/modular/dims.ts: -------------------------------------------------------------------------------- 1 | import wasmImport from "../wasm"; 2 | 3 | export let wasm: any = undefined; 4 | export async function init() { 5 | wasm = await wasmImport("modular/dims"); 6 | } 7 | init(); 8 | 9 | export function index(N) { 10 | return wasm.exports.wasm_index(N); 11 | } 12 | 13 | export function eulerPhi(N) { 14 | return wasm.exports.wasm_eulerPhi(N); 15 | } 16 | 17 | export function dimensionCuspForms(N) { 18 | return wasm.exports.wasm_dimensionCuspForms(N); 19 | } 20 | 21 | export function dimensionModularForms(N) { 22 | return wasm.exports.wasm_dimensionModularForms(N); 23 | } 24 | 25 | export function dimensionEisensteinSeries(N) { 26 | return wasm.exports.wasm_dimensionEisensteinSeries(N); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /lib/src/modular/elliptic-curve-test.ts: -------------------------------------------------------------------------------- 1 | import { EllipticCurve, init } from "./elliptic-curve"; 2 | 3 | beforeEach(async () => { 4 | await init(); 5 | }); 6 | 7 | test("sum of an coefficients up to 10000 for 11a", () => { 8 | // sage: sum(EllipticCurve('11a').anlist(10000)) 9 | // 216 10 | const E = EllipticCurve([0, -1, 1, -10, -20]); 11 | const v = E.anlist(10000); 12 | expect(v.reduce((a,b)=>a+b) == 212); 13 | }); 14 | -------------------------------------------------------------------------------- /lib/src/modular/elliptic-curve.test.ts: -------------------------------------------------------------------------------- 1 | import { EllipticCurve, init } from "./elliptic-curve"; 2 | 3 | beforeEach(async () => { 4 | await init(); 5 | }); 6 | 7 | test("sum of an coefficients up to 10000 for 11a", () => { 8 | // sage: sum(EllipticCurve('11a').anlist(10000)) 9 | // 216 10 | const E = EllipticCurve("11a"); 11 | const v = E.anlist(10000); 12 | expect(v.reduce((a, b) => a + b) == 212); 13 | }); 14 | 15 | test("sum of an coefficients up to 10^5 for 5077a", () => { 16 | const E = EllipticCurve("5077a"); 17 | const v = E.anlist(10 ^ 5); 18 | expect(v.reduce((a, b) => a + b) == 20905); 19 | }); 20 | 21 | test("conductors of some curves", () => { 22 | expect(EllipticCurve("11a").conductor() == 11); 23 | expect(EllipticCurve("37a").conductor() == 37); 24 | expect(EllipticCurve("389a").conductor() == 389); 25 | expect(EllipticCurve("5077a").conductor() == 5077); 26 | }); 27 | 28 | test("compute ap for a curve", () => { 29 | expect(EllipticCurve("389a").ap(2003) == 27); 30 | }); 31 | 32 | test("root numbers of some curves", () => { 33 | expect(EllipticCurve("11a").rootNumber() == 1); 34 | expect(EllipticCurve("37a").rootNumber() == -1); 35 | expect(EllipticCurve("389a").rootNumber() == 1); 36 | expect(EllipticCurve("5077a").rootNumber() == -1); 37 | expect(EllipticCurve([1, -1, 0, -79, 289]).rootNumber() == 1); 38 | }); 39 | 40 | test("analytic ranks of some curves", () => { 41 | expect(EllipticCurve("11a").analyticRank() == 0); 42 | expect(EllipticCurve("37a").analyticRank() == 1); 43 | expect(EllipticCurve("389a").analyticRank() == 2); 44 | expect(EllipticCurve("5077a").analyticRank() == 3); 45 | expect(EllipticCurve([1, -1, 0, -79, 289]).analyticRank() == 4); 46 | }); 47 | -------------------------------------------------------------------------------- /lib/src/modular/mat2x2.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn Mat2x2(comptime T: type) type { 4 | return struct { 5 | const Mat = @This(); 6 | a: T, 7 | b: T, 8 | c: T, 9 | d: T, 10 | pub fn init(a: T, b: T, c: T, d: T) Mat { 11 | return Mat{ .a = a, .b = b, .c = c, .d = d }; 12 | } 13 | pub fn print(self: Mat) void { 14 | std.debug.print("[{},{}; {},{}]\n", .{ self.a, self.b, self.c, self.d }); 15 | } 16 | pub fn eql(x: Mat, y: Mat) bool { 17 | return x.a == y.a and x.b == y.b and x.c == y.c and x.d == y.d; 18 | } 19 | }; 20 | } 21 | 22 | test "Make a matrix" { 23 | var m = Mat2x2(i32){ .a = 1, .b = 2, .c = 3, .d = 4 }; 24 | try std.testing.expect(m.eql(m)); 25 | var n = Mat2x2(i32){ .a = -1, .b = 2, .c = 3, .d = 4 }; 26 | try std.testing.expect(!m.eql(n)); 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/modular/p1list-export.zig: -------------------------------------------------------------------------------- 1 | const p1list = @import("./p1list.zig"); 2 | const std = @import("std"); 3 | const interface = @import("../interface.zig"); 4 | 5 | fn P1ListT(comptime T: type, N: i32) !p1list.P1List(T) { 6 | return p1list.P1List(T).init(interface.allocator(), N); 7 | } 8 | 9 | var p1lists32 = interface.ProxyObjects(p1list.P1List(i32)).init(); 10 | 11 | pub export fn P1List(N: i32) i32 { 12 | var P1 = P1ListT(i32, N) catch { 13 | interface.throw("P1List: failed to create P1ListT"); 14 | return -1; 15 | }; 16 | return p1lists32.put(P1) catch { 17 | interface.throw("P1List: failed to store P1 in map"); 18 | return -1; 19 | }; 20 | } 21 | 22 | pub export fn P1List_count(handle: i32) i32 { 23 | const P1 = p1lists32.get(handle) orelse { 24 | interface.throw("P1List_count: failed to get P1 with given handle"); 25 | return -1; 26 | }; 27 | return @intCast(i32, P1.count()); // TODO -- need error catching cast.. 28 | } 29 | 30 | pub export fn P1List_free(handle: i32) void { 31 | p1lists32.free(handle); 32 | } 33 | 34 | extern fn P1List_normalize_cb(u: i32, v: i32) void; 35 | pub export fn P1List_normalize(handle: i32, u: i32, v: i32) void { 36 | const P1 = p1lists32.get(handle) orelse { 37 | interface.throw("P1List_normalize: failed to get P1 with given handle"); 38 | return; 39 | }; 40 | const elt = P1.normalize(u, v) catch { 41 | interface.throw("P1List_normalize: failed to normalize"); 42 | return; 43 | }; 44 | P1List_normalize_cb(elt.u, elt.v); 45 | } 46 | 47 | extern fn P1List_normalize_with_scalar_cb(u: i32, v: i32, s: i32) void; 48 | pub export fn P1List_normalize_with_scalar(handle: i32, u: i32, v: i32) void { 49 | const P1 = p1lists32.get(handle) orelse { 50 | interface.throw("P1List_normalize_with_scalar: failed to get P1 with given handle"); 51 | return; 52 | }; 53 | const z = P1.normalizeWithScalar(u, v) catch { 54 | interface.throw("P1List_normalize_with_scalar: failed to normalize"); 55 | return; 56 | }; 57 | P1List_normalize_with_scalar_cb(z.u, z.v, z.s); 58 | } 59 | 60 | pub export fn P1List_index(handle: i32, u: i32, v: i32) i32 { 61 | const P1 = p1lists32.get(handle) orelse { 62 | interface.throw("P1List_index: failed to get P1 with given handle"); 63 | return 0; 64 | }; 65 | const i = P1.index(u, v) catch { 66 | interface.throw("P1List_index: failed to normalize and determine index of (u,v); is this element valid?"); 67 | return 0; 68 | }; 69 | return @intCast(i32, i); // TODO -- need error catching cast.. 70 | } 71 | 72 | pub export fn sparseMatrixBench(N: i32) void { 73 | @import("./sparse-matrix.zig").bench(N) catch { 74 | interface.throw("failed"); 75 | }; 76 | } 77 | 78 | pub export fn P1List_print(handle: i32) void { 79 | const P1 = p1lists32.get(handle) orelse { 80 | return; 81 | }; 82 | P1.print(); 83 | } 84 | -------------------------------------------------------------------------------- /lib/src/modular/p1list.test.ts: -------------------------------------------------------------------------------- 1 | import P1List, { init } from "./p1list"; 2 | 3 | beforeEach(async () => { 4 | await init(); 5 | }); 6 | 7 | test("number of elements of P1(11)", () => { 8 | expect(P1List(11).count()).toBe(12); 9 | }); 10 | 11 | test("some computations with P1(100)", () => { 12 | const P100 = P1List(100); 13 | expect(P100.count()).toBe(180); 14 | expect(P100.normalize(3, 7)).toEqual([1, 69]); 15 | expect(P100.normalize_with_scalar(3, 7)).toEqual([1, 69, 3]); 16 | expect((3 * 69) % 100).toBe(7); 17 | expect(P100.index(1, 69)).toBe(70); 18 | expect(P100.index(3, 7)).toBe(70); 19 | expect(P100.index(0, 1)).toBe(0); 20 | expect(P100.index(1, 0)).toBe(1); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/src/modular/p1list.ts: -------------------------------------------------------------------------------- 1 | import wasmImport from "../wasm"; 2 | 3 | // @ts-ignore -- typescript doesn't have FinalizationRegistry 4 | const registry = new FinalizationRegistry((handle) => { 5 | // console.log(`Freeing memory for ${handle}`); 6 | wasm.exports.P1List_free(handle); 7 | }); 8 | 9 | class P1ListClass { 10 | private readonly N: number; 11 | private readonly handle: number; 12 | 13 | constructor(N) { 14 | this.N = N; 15 | this.handle = wasm.exports.P1List(N); 16 | registry.register(this, this.handle); // so we get notified when garbage collected. 17 | } 18 | 19 | count(): number { 20 | return wasm.exports.P1List_count(this.handle); 21 | } 22 | 23 | normalize(u: number, v: number): [number, number] { 24 | wasm.exports.P1List_normalize(this.handle, u, v); 25 | return result; 26 | } 27 | 28 | normalize_with_scalar(u: number, v: number): [number, number] { 29 | wasm.exports.P1List_normalize_with_scalar(this.handle, u, v); 30 | return result; 31 | } 32 | 33 | index(u: number, v: number): number { 34 | return wasm.exports.P1List_index(this.handle, u, v); 35 | } 36 | 37 | __repr__(): string { 38 | return `P1List(${this.N})`; 39 | } 40 | 41 | print(): void { 42 | wasm.exports.P1List_print(this.handle); 43 | } 44 | } 45 | 46 | let result; 47 | function P1List_normalize_cb(u, v): void { 48 | result = [u, v]; 49 | } 50 | function P1List_normalize_with_scalar_cb(u, v, s): void { 51 | result = [u, v, s]; 52 | } 53 | 54 | export let wasm: any = undefined; 55 | export async function init() { 56 | wasm = await wasmImport("modular/p1list", { 57 | env: { P1List_normalize_cb, P1List_normalize_with_scalar_cb }, 58 | }); 59 | } 60 | init(); 61 | 62 | export default function P1List(N: number) { 63 | return new P1ListClass(N); 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/modular/sparse-matrix-interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const interface = @import("../interface.zig"); 3 | const sparse_matrix = @import("./sparse-matrix.zig"); 4 | const sparse_vector_interface = @import("./sparse-vector-interface.zig"); 5 | const errors = @import("../errors.zig"); 6 | 7 | const SparseMatrixType = sparse_matrix.SparseMatrixMod(i32); 8 | var SparseMatrix_objects = interface.ProxyObjects(SparseMatrixType).init(); 9 | 10 | pub export fn SparseMatrix_free(handle: i32) void { 11 | SparseMatrix_objects.free(handle); 12 | } 13 | 14 | pub fn SparseMatrix_get(handle: i32) !SparseMatrixType { 15 | return SparseMatrix_objects.get(handle) orelse { 16 | interface.throw("SparseMatrix: failed to get SparseMatrix with given handle"); 17 | return errors.General.RuntimeError; 18 | }; 19 | } 20 | 21 | pub fn SparseMatrix_put(v: SparseMatrixType) i32 { 22 | return SparseMatrix_objects.put(v) catch { 23 | interface.throw("SparseMatrix: failed to store"); 24 | return 0; 25 | }; 26 | } 27 | 28 | pub export fn SparseMatrix_stringify(handle: i32) void { 29 | SparseMatrix_objects.stringify(handle); 30 | } 31 | 32 | pub export fn SparseMatrix_format(handle: i32) void { 33 | SparseMatrix_objects.format(handle); 34 | } 35 | 36 | pub export fn SparseMatrix_getRow(handle: i32, row: i32) i32 { 37 | const x = SparseMatrix_get(handle) catch { 38 | return 0; 39 | }; 40 | var v = x.getRow(@intCast(usize, row)) catch { 41 | interface.throw("SparseMatrix: failed to allocate space for row"); 42 | return 0; 43 | }; 44 | return sparse_vector_interface.SparseVector_put(v); 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/modular/sparse-vector-interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const interface = @import("../interface.zig"); 3 | const sparse_vector = @import("./dense-vector.zig"); 4 | const errors = @import("../errors.zig"); 5 | 6 | const SparseVectorType = sparse_vector.SparseVectorMod(i32); 7 | var SparseVector_objects = interface.ProxyObjects(SparseVectorType).init(); 8 | 9 | pub export fn SparseVector_free(handle: i32) void { 10 | SparseVector_objects.free(handle); 11 | } 12 | 13 | pub fn SparseVector_get(handle: i32) !SparseVectorType { 14 | return SparseVector_objects.get(handle) orelse { 15 | interface.throw("SparseVector: failed to get SparseVector with given handle"); 16 | return errors.General.RuntimeError; 17 | }; 18 | } 19 | 20 | pub fn SparseVector_put(v: SparseVectorType) i32 { 21 | return SparseVector_objects.put(v) catch { 22 | interface.throw("SparseVector: failed to store"); 23 | return 0; 24 | }; 25 | } 26 | 27 | pub export fn SparseVector_stringify(handle: i32) void { 28 | SparseVector_objects.stringify(handle); 29 | } 30 | 31 | pub export fn SparseVector_format(handle: i32) void { 32 | SparseVector_objects.format(handle); 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/number.ts: -------------------------------------------------------------------------------- 1 | /* How numerical literals are parsed in JSage. 2 | 3 | For now: 4 | 5 | Integers = Arbitrary precision. 6 | Floats = normal javascript floats. 7 | 8 | As soon as we expose mpfr functionality (say), then 9 | use that here... but make sure to take into account 10 | the approach Robert Bradshaw implemented in Sage with 11 | real literals? 12 | */ 13 | 14 | import { IntegerClass } from "./integer/integer"; 15 | 16 | export function Number(s): IntegerClass | number { 17 | if (s.includes(".")) { 18 | return parseFloat(s); 19 | } else { 20 | return new IntegerClass(s, undefined, 10); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/pari/index.ts: -------------------------------------------------------------------------------- 1 | import wasmImport, { WasmInstance } from "../wasm"; 2 | 3 | export let wasm: WasmInstance | undefined = undefined; 4 | 5 | export function exec(str: string): string { 6 | if (wasm == null) throw Error("call pari.init() first"); 7 | return wasm.callWithString("exec", str) as string; 8 | } 9 | 10 | export async function init(parisize: number = 0, maxprime: number = 0) { 11 | wasm = await wasmImport("pari/pari.wasm", { 12 | init: (wasm) => wasm.exports.init(parisize, maxprime), 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/pari/interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pari = @import("./pari.zig"); 3 | 4 | extern fn wasmSendString(ptr: [*]const u8, len: usize) void; 5 | 6 | export fn exec(s: [*:0]const u8) void { 7 | var r = pari.exec(s) catch |err| { 8 | //todo 9 | std.debug.print("pari interface exec error: {}\n", .{err}); 10 | return; 11 | }; 12 | wasmSendString(r, std.mem.len(r)); 13 | std.c.free(r); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/pari/pari.h: -------------------------------------------------------------------------------- 1 | /* 2 | If there is anything from the PARI C library that you want to 3 | use, then you have to look in the pari header files and copy 4 | the relevant part here. Why? Because directly importing 5 | pari/pari.h into zig **crashes the zig compiler** -- presumably that's 6 | a bug/shortcoming in the Zig's cImport? 7 | 8 | --- 9 | 10 | We will probably need to *automate* copying this from pari header files, 11 | if we get serious, since things like enums, etc. could change and cause 12 | subtle bugs. 13 | */ 14 | 15 | #include // size_t 16 | 17 | typedef size_t sizet; 18 | typedef unsigned long ulong; 19 | typedef ulong pari_sp; 20 | typedef long *GEN; 21 | extern pari_sp avma; 22 | 23 | GEN stoi(long x); 24 | GEN dbltor(double s); 25 | GEN gadd(GEN x, GEN y); 26 | char *GENtostr(GEN x); // malloc'd and YOU must free it. 27 | GEN gp_read_str_multiline(const char *s, char *last); 28 | long itos(GEN x); 29 | void output(GEN x); 30 | void pari_init(size_t parisize, ulong maxprime); 31 | 32 | long rank(GEN x); 33 | GEN ker(GEN x); 34 | long lgcols(GEN x); // number of columns of t_MAT with at least one column 35 | long nbrows(GEN x); // number of columns of t_MAT with at least one column 36 | 37 | GEN lift(GEN x); 38 | long itos(GEN x); // x must be of type t_INT, 39 | 40 | GEN matsize(GEN x); 41 | 42 | GEN cgetg(long x, long y); 43 | 44 | GEN zeromatcopy(long m, long n); 45 | GEN stoi(long s); 46 | 47 | GEN gmodulss(long x, long y); 48 | 49 | GEN ellinit(GEN x, GEN D, long prec); 50 | GEN ellap(GEN E, GEN p); 51 | GEN ellan(GEN e, long n); 52 | GEN ellanalyticrank(GEN e, GEN eps, long prec); 53 | GEN ellanalyticrank_bitprec(GEN e, GEN eps, long bitprec); 54 | GEN ellQ_get_N(GEN e); 55 | long ellrootno_global(GEN e); 56 | 57 | enum { t_ELL_Rg = 0, t_ELL_Q, t_ELL_Qp, t_ELL_Fp, t_ELL_Fq, t_ELL_NF }; 58 | 59 | // These are copied from pari's headers/parigen.h, which says they are SUBJECT TO CHANGE. 60 | // So watch out! 61 | enum { 62 | t_INT = 1, 63 | t_REAL = 2, 64 | t_INTMOD = 3, 65 | t_FRAC = 4, 66 | t_FFELT = 5, 67 | t_COMPLEX= 6, 68 | t_PADIC = 7, 69 | t_QUAD = 8, 70 | t_POLMOD = 9, 71 | t_POL = 10, 72 | t_SER = 11, 73 | t_RFRAC = 13, 74 | t_QFR = 15, 75 | t_QFI = 16, 76 | t_VEC = 17, 77 | t_COL = 18, 78 | t_MAT = 19, 79 | t_LIST = 20, 80 | t_STR = 21, 81 | t_VECSMALL= 22, 82 | t_CLOSURE = 23, 83 | t_ERROR = 24, 84 | t_INFINITY= 25 85 | }; 86 | 87 | typedef unsigned char *byteptr; 88 | /* iterator over primes */ 89 | typedef struct { 90 | int strategy; /* 1 to 4 */ 91 | GEN bb; /* iterate through primes <= bb */ 92 | ulong c, q; /* congruent to c (mod q) */ 93 | 94 | /* strategy 1: private prime table */ 95 | byteptr d; /* diffptr + n */ 96 | ulong p; /* current p = n-th prime */ 97 | ulong b; /* min(bb, ULONG_MAX) */ 98 | 99 | /* strategy 2: sieve, use p */ 100 | struct pari_sieve *psieve; 101 | unsigned char *sieve, *isieve; 102 | ulong cache[9]; /* look-ahead primes already computed */ 103 | ulong chunk; /* # of odd integers in sieve */ 104 | ulong a, end, sieveb; /* [a,end] interval currently being sieved, 105 | * end <= sieveb = min(bb, maxprime^2, ULONG_MAX) */ 106 | ulong pos, maxpos; /* current cell and max cell */ 107 | 108 | /* strategy 3: unextprime, use p */ 109 | 110 | /* strategy 4: nextprime */ 111 | GEN pp; 112 | } forprime_t; 113 | int forprime_init(forprime_t *T, GEN a, GEN b); 114 | GEN forprime_next(forprime_t *T); 115 | 116 | -------------------------------------------------------------------------------- /lib/src/pari/pari.test.ts: -------------------------------------------------------------------------------- 1 | import { exec, init } from "./index"; 2 | 3 | beforeEach(async () => { 4 | await init(); 5 | }); 6 | 7 | test("add 2+3", async () => { 8 | expect(exec("2+3")).toBe("5"); 9 | }); 10 | 11 | test("sum integers up to 100", async () => { 12 | expect( 13 | exec(`{s=0; 14 | for(i=1,100, 15 | s += i 16 | ); 17 | s}`) 18 | ).toBe("5050"); 19 | }); 20 | 21 | test("factor this year", async () => { 22 | expect(exec("factor(2021)")).toBe("\n[43 1]\n\n[47 1]\n"); 23 | }); 24 | -------------------------------------------------------------------------------- /lib/src/pari/pari.zig: -------------------------------------------------------------------------------- 1 | pub const c = @cImport(@cInclude("pari.h")); 2 | const std = @import("std"); 3 | pub const General = error{OverflowError}; 4 | 5 | // The headers that define this are too hard for zig to parse due to macros. 6 | extern fn pari_init_opts(parisize: c.sizet, maxprime: c.ulong, flags: c_int) void; 7 | 8 | var didInit = false; 9 | pub export fn init(parisize: c.sizet, maxprime: c.ulong) void { 10 | if (didInit) return; 11 | didInit = true; 12 | // We must use pari_init_opts rather than just pari_init, so we can set 13 | // different options (the last argument). 14 | const OPTIONS = 4; // this is INIT_DFTm = "initialize the defaults" -- header that has this is too hard to parse. 15 | pari_init_opts(if (parisize == 0) 64 * 10000000 else parisize, maxprime, OPTIONS); 16 | } 17 | 18 | pub fn exec(s: [*:0]const u8) ![*:0]u8 { 19 | const context = Context(); 20 | defer context.deinit(); 21 | var x: c.GEN = c.gp_read_str_multiline(s, null); 22 | return c.GENtostr(x); 23 | } 24 | 25 | const ContextType = struct { 26 | av: c.pari_sp, 27 | pub fn deinit(C: ContextType) void { 28 | c.avma = C.av; 29 | } 30 | }; 31 | 32 | pub fn Context() ContextType { 33 | init(0, 0); 34 | return ContextType{ .av = c.avma }; 35 | } 36 | 37 | // This is equivalent to "gcoeff(z, i, j) = x" in pari library code, 38 | // but we have to rewrite because gcoeff is a macro. 39 | pub fn setcoeff2(z: c.GEN, i: usize, j: usize, x: c.GEN) void { 40 | @ptrCast([*][*]c.GEN, z)[j][i] = x; 41 | } 42 | 43 | pub fn getcoeff2(z: c.GEN, i: usize, j: usize) c.GEN { 44 | return @ptrCast([*][*]c.GEN, z)[j][i]; 45 | } 46 | 47 | pub fn setcoeff1(z: c.GEN, i: usize, x: c.GEN) void { 48 | @ptrCast([*]c.GEN, z)[i] = x; 49 | } 50 | 51 | pub fn getcoeff1(z: c.GEN, i: usize) c.GEN { 52 | return @ptrCast([*]c.GEN, z)[i]; 53 | } 54 | 55 | const expect = std.testing.expect; 56 | 57 | test "calling exec" { 58 | init(0, 0); 59 | const code = "nextprime(2021)"; 60 | var r: [*:0]u8 = try exec(code); 61 | try expect(std.cstr.cmp(r, "2027") == 0); 62 | std.debug.print("exec('{s}') = {s}\n", .{ code, r }); 63 | std.c.free(r); 64 | } 65 | 66 | const BENCH = false; 67 | test "a little benchmark" { 68 | if (BENCH) { 69 | const time = std.time.milliTimestamp; 70 | const t = time(); 71 | var i: usize = 0; 72 | while (i < 10) : (i += 1) { 73 | std.debug.print("pi(10^8) = {s}\n", .{try exec("primepi(10^8)")}); 74 | } 75 | std.debug.print("{}ms\n", .{time() - t}); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/python/index.ts: -------------------------------------------------------------------------------- 1 | import wasmImport, { WasmInstance } from "../wasm"; 2 | 3 | export let wasm: WasmInstance | undefined = undefined; 4 | 5 | export function exec(str: string): void { 6 | if (wasm == null) throw Error("call init"); 7 | wasm.callWithString("exec", str); 8 | } 9 | 10 | export function repr(str: string): string { 11 | if (wasm == null) throw Error("call init"); 12 | return wasm.callWithString("eval", str) as string; 13 | } 14 | 15 | // export function toObject(str: string): object { 16 | // if (wasm == null) throw Error("call init"); 17 | // //return JSON.parse(wasm.callWithString("toJSON", str)); 18 | // } 19 | 20 | export async function init() { 21 | if (wasm != null) return; 22 | wasm = await wasmImport("python/python.wasm", { 23 | init: (wasm) => wasm.exports.init(), 24 | }); 25 | } 26 | 27 | init(); 28 | -------------------------------------------------------------------------------- /lib/src/python/interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const python = @import("./python.zig"); 3 | const interface = @import("../interface.zig"); 4 | 5 | export fn init() void { 6 | python.init(); 7 | } 8 | 9 | export fn exec(s: [*:0]const u8) void { 10 | python.exec(s) catch |err| { 11 | //todo 12 | std.debug.print("python error: '{}'\nwhen evaluating '{s}'", .{ err, s }); 13 | return; 14 | }; 15 | } 16 | 17 | extern fn wasmSendString(ptr: [*]const u8, len: usize) void; 18 | export fn eval(s: [*:0]const u8) void { 19 | const r = python.eval(interface.allocator(), s) catch |err| { 20 | //todo 21 | std.debug.print("python error: '{}'\nwhen evaluating '{s}'", .{ err, s }); 22 | return; 23 | }; 24 | defer interface.allocator().free(r); 25 | // Todo: this r[0..1] is a casting hack -- I think it's harmless 26 | // because r itself is null terminated (?). 27 | const ptr: [*]const u8 = r[0..1]; 28 | wasmSendString(ptr, std.mem.len(r)); 29 | } 30 | 31 | // export fn toJSON(s: [*:0]const u8) void { 32 | // const r = python.toJSON(interface.allocator(), s) catch |err| { 33 | // //todo 34 | // std.debug.print("python error: '{}'\nwhen exporting '{s}' to JSON", .{ err, s }); 35 | // return; 36 | // }; 37 | // defer interface.allocator().free(r); 38 | // const ptr: [*]const u8 = r[0..1]; 39 | // wasmSendString(ptr, std.mem.len(r)); 40 | // } 41 | -------------------------------------------------------------------------------- /lib/src/python/python.test-disabled.ts: -------------------------------------------------------------------------------- 1 | import { exec, repr, init } from "./index"; 2 | 3 | beforeEach(async () => { 4 | await init(); 5 | }); 6 | 7 | test("add 2+3", async () => { 8 | exec("a = 2+3"); 9 | expect(repr("a")).toBe("5"); 10 | }); 11 | 12 | test("Exec involving multiple statements", async () => { 13 | exec(` 14 | def f(n): 15 | s = 0 16 | for i in range(1,n+1): 17 | s += i 18 | return s 19 | `); 20 | expect(repr("f(100)")).toBe("5050"); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/src/random.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | var p: std.rand.DefaultPrng = undefined; 4 | var initialized: bool = false; 5 | pub fn seededPrng() !std.rand.DefaultPrng { 6 | if (initialized) { 7 | return p; 8 | } 9 | // std.debug.print("initializing...\n", .{}); 10 | p = std.rand.DefaultPrng.init(blk: { 11 | var seed: u64 = undefined; 12 | try std.os.getrandom(std.mem.asBytes(&seed)); 13 | break :blk seed; 14 | }); 15 | initialized = true; 16 | return p; 17 | } 18 | 19 | const expect = std.testing.expect; 20 | 21 | test "can make a random number" { 22 | var prng = try seededPrng(); 23 | var random = prng.random(); 24 | var r = random.intRangeLessThan(i32, 0, 5); 25 | try expect(r >= 0); 26 | try expect(r < 5); 27 | } 28 | 29 | test "try twice" { 30 | var s1 = try seededPrng(); 31 | var s2 = try seededPrng(); 32 | try std.testing.expectEqual(s1.s, s2.s); 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/rational/index.ts: -------------------------------------------------------------------------------- 1 | import Rational, { init } from "./rational"; 2 | export { Rational, init }; 3 | import QQ from "./rational-field"; 4 | export { QQ }; 5 | -------------------------------------------------------------------------------- /lib/src/rational/rational-field.ts: -------------------------------------------------------------------------------- 1 | // The field QQ. 2 | 3 | import CallableInstance from "callable-instance"; // this is just a few lines of code 4 | import { RationalNumber } from "./rational"; 5 | 6 | class RationalField extends CallableInstance<[number], string> { 7 | constructor() { 8 | super("element"); 9 | } 10 | 11 | __str__() { 12 | return "Rational Field"; 13 | } 14 | 15 | element(n: number | string, base: number = 10): RationalNumber { 16 | return new RationalNumber(n, base); 17 | } 18 | } 19 | 20 | const QQ = new RationalField(); 21 | 22 | export default QQ; 23 | -------------------------------------------------------------------------------- /lib/src/time.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn time() u64 { 4 | var timer = std.time.Timer.start() catch { 5 | return 0; 6 | }; 7 | return timer.lap(); 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/timer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | // std.time.milliTimestamp; 3 | 4 | const Timer = struct { 5 | t: i64, 6 | verbose: bool, 7 | pub fn print(self: *Timer, s: []const u8) void { 8 | if (!self.verbose) return; 9 | const now = std.time.milliTimestamp(); 10 | std.debug.print("{}ms:\t {s}\n", .{ now - self.t, s }); 11 | self.t = now; 12 | } 13 | }; 14 | 15 | pub fn timer(verbose: bool) Timer { 16 | return Timer{ .t = std.time.milliTimestamp(), .verbose = verbose }; 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/util.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const List = std.ArrayList; 3 | 4 | pub fn range(allocator: std.mem.Allocator, comptime T: type, n: usize) !List(T) { 5 | var v = try List(T).initCapacity(allocator, n); 6 | var i: T = 0; 7 | while (i < n) : (i += 1) { 8 | try v.append(i); 9 | } 10 | return v; 11 | } 12 | 13 | pub fn constantList(allocator: std.mem.Allocator, x: anytype, n: usize) !List(@TypeOf(x)) { 14 | const T = @TypeOf(x); 15 | var v = try List(T).initCapacity(allocator, n); 16 | var i: usize = 0; 17 | while (i < n) : (i += 1) { 18 | try v.append(x); 19 | } 20 | return v; 21 | } 22 | 23 | const expect = std.testing.expect; 24 | const allocator0 = std.testing.allocator; 25 | 26 | test "create a range" { 27 | const v = try range(allocator0, i16, 10); 28 | defer v.deinit(); 29 | try expect(v.items.len == 10); 30 | //std.debug.print("{}\n", .{v}); 31 | } 32 | 33 | test "create a bigger range and check some things" { 34 | const v = try range(allocator0, i32, 100000); 35 | defer v.deinit(); 36 | try expect(v.items.len == 100000); 37 | try expect(v.items[0] == 0); 38 | try expect(v.items[99999] == 99999); 39 | } 40 | 41 | test "create a constant list" { 42 | const v = try constantList(allocator0, @as(i16, 5), 3); 43 | defer v.deinit(); 44 | try expect(v.items.len == 3); 45 | try expect(v.items[0] == 5); 46 | try expect(v.items[1] == 5); 47 | try expect(v.items[2] == 5); 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/zig-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #set -ev 3 | 4 | export SRC="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 5 | export PACKAGES="$SRC"/../../packages 6 | export GMP_NATIVE="$PACKAGES"/gmp/dist/native 7 | export PARI_NATIVE="$PACKAGES"/pari/dist/native 8 | export MPFR_NATIVE="$PACKAGES"/mpfr/dist/native 9 | 10 | export DYLD_LIBRARY_PATH="$GMP_NATIVE/lib":"$PARI_NATIVE/lib":"$MPFR_NATIVE/lib" 11 | export LD_LIBRARY_PATH="$GMP_NATIVE/lib":"$PARI_NATIVE/lib":"$MPFR_NATIVE/lib" 12 | export ZIG_SYSTEM_LINKER_HACK=1 13 | 14 | #echo $DYLD_LIBRARY_PATH 15 | #echo $@ 16 | zig test --main-pkg-path "$SRC" $@ 17 | -------------------------------------------------------------------------------- /lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../packages/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "dist" 6 | }, 7 | "exclude": ["node_modules", "build", "dist"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/eclib/Makefile: -------------------------------------------------------------------------------- 1 | BUILD = build 2 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 3 | DIST = ${CWD}/dist 4 | DIST_NATIVE = ${DIST}/native 5 | DIST_WASM = ${DIST}/wasm 6 | DIST_JS = ${DIST}/js 7 | ZCC = ${CWD}/../../bin/zcc 8 | ZXX = ${CWD}/../../bin/z++ 9 | 10 | GMP = ${CWD}/../gmp/dist 11 | MPFR = ${CWD}/../mpfr/dist 12 | PARI = ${CWD}/../pari/dist 13 | NTL = ${CWD}/../ntl/dist 14 | FLINT = ${CWD}/../flint/dist 15 | 16 | all: ${DIST_NATIVE}/.built ${DIST_WASM}/.built 17 | 18 | ${BUILD}/eclib: 19 | mkdir -p ${BUILD} 20 | cd ${BUILD} && git clone https://github.com/JohnCremona/eclib.git && cd eclib 21 | 22 | .PHONY: source 23 | source: ${BUILD}/eclib 24 | 25 | 26 | ## Native library 27 | 28 | ${BUILD}/native: ${BUILD}/eclib 29 | rm -rf ${BUILD}/native 30 | cd ${BUILD} && git clone eclib native && cd native && ./autogen.sh 31 | 32 | ${DIST_NATIVE}/.built: ${BUILD}/native 33 | cd ${BUILD}/native && \ 34 | CXXFLAGS="-I${GMP}/native/include -I${MPFR}/native/include" LDFLAGS="-L${GMP}/native/lib -L${MPFR}/native/lib" \ 35 | CC="zig cc" CXX="zig c++" AR="zig ar" RANLIB="zig ranlib" \ 36 | ./configure \ 37 | --with-pari=${PARI}/native \ 38 | --with-ntl=${NTL}/native \ 39 | --with-flint=${FLINT}/native \ 40 | --prefix=${DIST_NATIVE} 41 | # A hack -- change the empty BOOST_LIBS to let us link in gmp. 42 | cd ${BUILD}/native && make -j8 BOOST_LIBS="-lgmp -lmpfr" PTHREAD_CFLAGS="" 43 | cd ${BUILD}/native && make BOOST_LIBS="-lgmp -lmpfr" PTHREAD_CFLAGS="" install 44 | touch ${DIST_NATIVE}/.built 45 | 46 | .PHONY: native 47 | native: ${DIST_NATIVE}/.built 48 | 49 | 50 | ## WebAssembly library 51 | 52 | ${BUILD}/wasm: ${BUILD}/eclib 53 | rm -rf ${BUILD}/wasm 54 | cd ${BUILD} && git clone eclib wasm && cd wasm && ./autogen.sh 55 | 56 | ${DIST_WASM}/.built: ${BUILD}/wasm 57 | cd ${BUILD}/wasm && \ 58 | CXXFLAGS="-I${GMP}/wasm/include -I${MPFR}/wasm/include" LDFLAGS="-L${GMP}/wasm/lib -L${MPFR}/wasm/lib" \ 59 | LIBS="-lgmp -lmpfr" \ 60 | CC=${ZCC} CXX=${ZXX} AR="zig ar" RANLIB="zig ranlib" \ 61 | ./configure \ 62 | --with-pari=${PARI}/wasm \ 63 | --with-ntl=${NTL}/wasm \ 64 | --with-flint=${FLINT}/wasm \ 65 | --prefix=${DIST_WASM} \ 66 | --enable-shared=no \ 67 | --enable-static=yes \ 68 | --with-boost=no 69 | # We change PTHREAD_CFLAGS, which would be -pthread, which would break linking with all 70 | # our other libraries. Also a hack -- change the empty BOOST_LIBS to let us link in gmp. 71 | cd ${BUILD}/wasm && make -j8 PTHREAD_CFLAGS="" BOOST_LIBS="-lgmp -lmpfr ${GMP}/wasm/lib/libgmp.a ${PARI}/wasm/lib/libpari.a ${NTL}/wasm/lib/libntl.a ${FLINT}/wasm/lib/libflint.a ${MPFR}/wasm/lib/libmpfr.a " 72 | cd ${BUILD}/wasm && make PTHREAD_CFLAGS="" BOOST_LIBS="-lgmp -lmpfr" install 73 | cp -v ${BUILD}/wasm/progs/*.wasm ${DIST_WASM}/bin 74 | touch ${DIST_WASM}/.built 75 | 76 | .PHONY: wasm 77 | wasm: ${DIST_WASM}/.built 78 | 79 | clean: 80 | rm -rf ${DIST} ${BUILD} node_module -------------------------------------------------------------------------------- /packages/flint/Makefile: -------------------------------------------------------------------------------- 1 | # See https://github.com/wbhart/flint2/tags 2 | VERSION = 2.8.4 3 | 4 | BUILD = build 5 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 6 | DIST_NATIVE = ${CWD}/dist/native 7 | DIST_WASM = ${CWD}/dist/wasm 8 | DIST_JS = ${CWD}/dist/js 9 | 10 | GMP = ${CWD}/../gmp/dist 11 | MPFR = ${CWD}/../mpfr/dist 12 | NTL = ${CWD}/../ntl/dist 13 | 14 | all: ${DIST_NATIVE}/.built ${DIST_WASM}/.built 15 | 16 | ${BUILD}/flint-${VERSION}.tar.gz: 17 | mkdir -p ${BUILD} 18 | cd ${BUILD} && curl https://codeload.github.com/wbhart/flint2/tar.gz/refs/tags/v${VERSION} -o flint-${VERSION}.tar.gz 19 | 20 | .PHONY: source 21 | source: ${BUILD}/flint 22 | 23 | 24 | ## Native library 25 | 26 | ${BUILD}/native: ${BUILD}/flint-${VERSION}.tar.gz 27 | rm -rf ${BUILD}/native 28 | cd ${BUILD} && mkdir native && tar xf flint-${VERSION}.tar.gz -C native --strip-components=1 29 | 30 | ${DIST_NATIVE}/.built: ${BUILD}/native 31 | cd ${BUILD}/native && CC="zig cc" CXX="zig c++" AR="zig ar" RANLIB="zig ranlib" \ 32 | ./configure \ 33 | --disable-shared \ 34 | --with-mpfr=${MPFR}/native \ 35 | --with-gmp=${GMP}/native \ 36 | --with-ntl=${NTL}/native \ 37 | --prefix=${DIST_NATIVE} 38 | cd ${BUILD}/native && make -j12 install # *enormous* number of tiny files! 39 | touch ${DIST_NATIVE}/.built 40 | 41 | .PHONY: native 42 | native: ${DIST_NATIVE}/.built 43 | 44 | 45 | ## WebAssembly library 46 | 47 | ${BUILD}/wasm: ${BUILD}/flint-${VERSION}.tar.gz 48 | rm -rf ${BUILD}/wasm 49 | cd ${BUILD} && mkdir wasm && tar xf flint-${VERSION}.tar.gz -C wasm --strip-components=1 50 | cd ${BUILD}/wasm && patch -p1 < ${CWD}/patches/ulong.patch # https://github.com/wbhart/flint2/pull/1020 51 | 52 | ${DIST_WASM}/.built: ${BUILD}/wasm 53 | cd ${BUILD}/wasm && CC="zig cc -target wasm32-wasi" CXX="zig c++ -target wasm32-wasi" AR="zig ar" RANLIB="zig ranlib" \ 54 | CFLAGS="-D_WASI_EMULATED_PROCESS_CLOCKS" \ 55 | ./configure \ 56 | --disable-shared \ 57 | --disable-pthread \ 58 | --with-mpfr=${MPFR}/wasm \ 59 | --with-gmp=${GMP}/wasm \ 60 | --with-ntl=${NTL}/wasm \ 61 | --prefix=${DIST_WASM} 62 | cd ${BUILD}/wasm && make -j12 install # *enormous* number of tiny files! 63 | touch ${DIST_WASM}/.built 64 | 65 | .PHONY: wasm 66 | wasm: ${DIST_WASM}/.built 67 | 68 | clean: 69 | rm -rf ${DIST_NATIVE} ${DIST_WASM} ${DIST_JS} ${CWD}/dist/tsconfig.tsbuildinfo ${BUILD}/native ${BUILD}/wasm node_modules -------------------------------------------------------------------------------- /packages/flint/patches/ulong.patch: -------------------------------------------------------------------------------- 1 | commit 939d5e9547af4b3b69cd8e02e8dacde32e935b47 2 | Author: William Stein 3 | Date: Wed Nov 3 12:47:09 2021 -0700 4 | 5 | Use the same ulong workaround as in nmod_vec.h 6 | 7 | For https://github.com/sagemathinc/JSage, I'm building FLINT for WebAssembly against GMP with long long limbs (WASM has 32-bit pointers but 64-bit arithmetic, so it WASM GMP is much faster when built with long long limbs). This ends up conflicting with how "ulong" is defined internally in FLINT. FLINT already has a workaround for this sort of problem in the file nmod_vec.h, but not in qsieve/factor.c. I'm proposing using the same workaround there. 8 | 9 | diff --git a/qsieve/factor.c b/qsieve/factor.c 10 | index d73a6f3d7..ea9c048e7 100644 11 | --- a/qsieve/factor.c 12 | +++ b/qsieve/factor.c 13 | @@ -21,6 +21,8 @@ 14 | #include 15 | #include 16 | 17 | +#undef ulong 18 | +#define ulong ulongxx /* interferes with system includes */ 19 | #include 20 | #if (!defined (__WIN32) || defined(__CYGWIN__)) && !defined(_MSC_VER) 21 | #include 22 | @@ -46,7 +48,7 @@ void qsieve_factor(fmpz_factor_t factors, const fmpz_t n) 23 | { 24 | qs_t qs_inf; 25 | mp_limb_t small_factor, delta; 26 | - ulong expt = 0; 27 | + ulongxx expt = 0; 28 | unsigned char * sieve; 29 | slong ncols, nrows, i, j = 0, count, relation = 0, num_primes; 30 | uint64_t * nullrows = NULL; 31 | @@ -195,7 +197,7 @@ void qsieve_factor(fmpz_factor_t factors, const fmpz_t n) 32 | qs_inf->num_handles = flint_request_threads(&qs_inf->handles, flint_get_num_threads()); 33 | 34 | /* ensure cache lines don't overlap if num_handles > 0 */ 35 | - sieve = flint_malloc((qs_inf->sieve_size + sizeof(ulong) 36 | + sieve = flint_malloc((qs_inf->sieve_size + sizeof(ulongxx) 37 | + (qs_inf->num_handles > 0 ? 64 : 0))*(qs_inf->num_handles + 1)); 38 | 39 | #if FLINT_USES_PTHREAD 40 | -------------------------------------------------------------------------------- /packages/gf2x/Makefile: -------------------------------------------------------------------------------- 1 | # See https://gitlab.inria.fr/gf2x/gf2x/-/releases and copy the link under "other" 2 | # for "gf2x-xxxx.tar.gz (source tarball, ready to `./configure ; make`)" 3 | # It has a sha1 so we can't just set the env var to a version. 4 | GF2X = https://gitlab.inria.fr/gf2x/gf2x/uploads/c46b1047ba841c20d1225ae73ad6e4cd/gf2x-1.3.0.tar.gz 5 | 6 | BUILD = build 7 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 8 | DIST_NATIVE = ${CWD}/dist/native 9 | DIST_WASM = ${CWD}/dist/wasm 10 | DIST_JS = ${CWD}/dist/js 11 | ZCC = ${CWD}/../../bin/zcc 12 | 13 | all: ${DIST_NATIVE}/.built ${DIST_WASM}/.built 14 | 15 | ${BUILD}/gf2x.tar.gz: 16 | mkdir -p ${BUILD} 17 | cd ${BUILD} && curl ${GF2X} -o gf2x.tar.gz 18 | 19 | .PHONY: source 20 | source: ${BUILD}/gf2x.tar.gz 21 | 22 | ## Native library 23 | 24 | ${BUILD}/native: ${BUILD}/gf2x.tar.gz 25 | rm -rf ${BUILD}/native 26 | cd ${BUILD} && mkdir native && tar xvf gf2x.tar.gz -C native --strip-components=1 27 | 28 | ${DIST_NATIVE}/.built: ${BUILD}/native 29 | cd ${BUILD}/native && CC="zig cc" AR="zig ar" CFLAGS="-O3" ./configure --prefix=${DIST_NATIVE} && make install 30 | touch ${DIST_NATIVE}/.built 31 | 32 | .PHONY: native 33 | native: ${DIST_NATIVE}/.built 34 | 35 | test-native: native 36 | cd ${BUILD}/native && make check 37 | 38 | 39 | ## WebAssembly library 40 | 41 | ${BUILD}/wasm: ${BUILD}/gf2x.tar.gz 42 | rm -rf ${BUILD}/wasm 43 | cd ${BUILD} && mkdir wasm && tar xvf gf2x.tar.gz -C wasm --strip-components=1 44 | 45 | ${DIST_WASM}/.built: ${BUILD}/wasm 46 | cd ${BUILD}/wasm && CC=${ZCC} AR="zig ar" RANLIB="zig ranlib" CFLAGS="-O3" \ 47 | ./configure --build i686-pc-linux-gnu --host=none --prefix=${DIST_WASM} 48 | cd ${BUILD}/wasm && make install 49 | touch ${DIST_WASM}/.built 50 | 51 | .PHONY: wasm 52 | wasm: ${DIST_WASM}/.built 53 | 54 | clean: 55 | rm -rf ${DIST_NATIVE} ${DIST_WASM} ${DIST_JS} ${CWD}/dist/tsconfig.tsbuildinfo ${BUILD}/native ${BUILD}/wasm node_modules -------------------------------------------------------------------------------- /packages/gmp/README.md: -------------------------------------------------------------------------------- 1 | # GMP 2 | 3 | 4 | 5 | ## 64-bit 6 | 7 | I learned from 8 | 9 | https://github.com/torquem-ch/gmp-wasm/commit/48fec192ca77d2a8a874f973f469d4c16b21f034 10 | 11 | how to enable 64-bit limbs. In some benchmarks, this VERY significantly 12 | improves the speed (it's over twice as fast). -------------------------------------------------------------------------------- /packages/gmp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sagemath/gmp", 3 | "version": "1.0.0", 4 | "description": "GNU Multiprecision Arithmetic", 5 | "exports": { "./*": "./dist/js/*.js" }, 6 | "type": "module", 7 | "files": [ 8 | "dist/js/*", 9 | "README.md", 10 | "src/*", 11 | "package.json", 12 | "tsconfig.json", 13 | "Makefile" 14 | ], 15 | "scripts": { 16 | "build": "make", 17 | "clean": "make clean" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/sagemathinc/jsage.git" 22 | }, 23 | "keywords": ["number", "theory"], 24 | "author": "William Stein", 25 | "license": "LGPL-3.0-or-later", 26 | "bugs": { 27 | "url": "https://github.com/sagemathinc/jsage/issues" 28 | }, 29 | "homepage": "https://github.com/sagemathinc/jsage/tree/main/packages/gmp", 30 | "dependencies": {}, 31 | "devDependencies": {} 32 | } 33 | -------------------------------------------------------------------------------- /packages/gmp/patches/long-long-abi.patch: -------------------------------------------------------------------------------- 1 | commit 48fec192ca77d2a8a874f973f469d4c16b21f034 2 | Author: yperbasis 3 | Date: Wed Dec 16 11:15:35 2020 +0100 4 | 5 | Add longlong generic ABI 6 | 7 | diff --git a/configure b/configure 8 | index c27d6a8..ee29fa7 100755 9 | --- a/configure 10 | +++ b/configure 11 | @@ -4078,7 +4078,9 @@ echo "define_not_for_expansion(\`HAVE_HOST_CPU_$tmp_host')" >> $gmp_tmpconfigm4p 12 | 13 | 14 | # abilist needs to be non-empty, "standard" is just a generic name here 15 | -abilist="standard" 16 | +abilist="longlong standard" 17 | +# longlong ABI means mp_limb_t is (unsigned) long long 18 | +limb_longlong=longlong 19 | 20 | # FIXME: We'd like to prefer an ANSI compiler, perhaps by preferring 21 | # c89 over cc here. But note that on HP-UX c89 provides a castrated 22 | -------------------------------------------------------------------------------- /packages/jpython/.npmignore: -------------------------------------------------------------------------------- 1 | *.term -------------------------------------------------------------------------------- /packages/jpython/.prettierignore: -------------------------------------------------------------------------------- 1 | dev 2 | release 3 | *.json 4 | *.pytest_cache* 5 | dist -------------------------------------------------------------------------------- /packages/jpython/.yapfignore: -------------------------------------------------------------------------------- 1 | test/newlines.py 2 | test/jsage.py 3 | 4 | src/baselib/* 5 | src/lib/* 6 | 7 | test/aes_vectors.py 8 | test/regexp.py 9 | test/classes.py 10 | test/str.py 11 | test/functions.py 12 | test/generic.py 13 | test/imports.py 14 | test/annotations.py 15 | test/decorators.py 16 | test/docstrings.py 17 | test/loops.py 18 | test/lint.py 19 | test/starargs.py 20 | -------------------------------------------------------------------------------- /packages/jpython/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ## Main Developers 2 | 3 | William Stein (SageMath, Inc.) 4 | 5 | ## Other Contributors 6 | 7 | See https://github.com/williamstein/jpython/graphs/contributors 8 | 9 | - Kovid Goyal (main developer of RapydScript-ng) 10 | - Alexander Tsepkov (main developer of the original RapydScript) 11 | - Charles Law 12 | - Tobias Weber 13 | - Salvatore Di Dio 14 | - Tuomas Laakkonen 15 | -------------------------------------------------------------------------------- /packages/jpython/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-, William Stein 2 | Copyright (c) 2015-2020, Kovid Goyal 3 | Copyright (c) 2013-2014, Alexander Tsepkov 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /packages/jpython/Makefile: -------------------------------------------------------------------------------- 1 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | DIST = ${CWD}/dist 3 | 4 | all: ${DIST}/.built 5 | 6 | ${DIST}/.built: 7 | npm ci 8 | npm run build 9 | npm run test 10 | ln -sf ${CWD}/bin/jsage ${CWD}/../../bin/jsage 11 | ln -sf ${CWD}/bin/jpython ${CWD}/../../bin/jpython 12 | touch ${DIST}/.built 13 | 14 | clean: 15 | rm -rf ${DIST} node_modules -------------------------------------------------------------------------------- /packages/jpython/bench/README.md: -------------------------------------------------------------------------------- 1 | # Python Microbenchmarks 2 | 3 | This is a collection of microbenchmarks to help understand where jpython 4 | is fast and detect situations where it is 100x slower than it should be 5 | to ensure that it is usable. You can run any particular benchmark foo.py in jpython, 6 | python3 or pypy by typing: 7 | 8 | ```sh 9 | $ jpython foo.py 10 | $ python3 foo.py 11 | $ pypy foo.py 12 | ``` 13 | 14 | Many of these benchmarks are silly microbenchmarks that can be optimized 15 | out with an optimizing compiler, but others are kind of interesting, like 16 | the xgcd's with numbers. 17 | 18 | You can run all of the benchmarks via 19 | 20 | ```sh 21 | $ jpython all.py 22 | $ python3 all.py 23 | $ pypy all.py 24 | ``` 25 | -------------------------------------------------------------------------------- /packages/jpython/bench/all.py: -------------------------------------------------------------------------------- 1 | import misc 2 | import brython 3 | import numbers 4 | import pystone 5 | import p1list 6 | import nbody 7 | import uuid_ 8 | import fib 9 | import lambda_ 10 | import call 11 | import mandel 12 | import mypyc_micro 13 | 14 | from time import time 15 | from bench import all 16 | 17 | 18 | def run_all_benchmarks(): 19 | t = time() 20 | all() 21 | print("=" * 20) 22 | print("Grand total time: ", int((time() - t) * 1000), "ms") 23 | 24 | 25 | if __name__ == '__main__': 26 | run_all_benchmarks() 27 | -------------------------------------------------------------------------------- /packages/jpython/bench/bench.py: -------------------------------------------------------------------------------- 1 | # mypy 2 | from typing import Callable 3 | 4 | 5 | def time(f: Callable, *args) -> int: 6 | from time import time as time0 7 | t = time0() 8 | f(*args) 9 | return int((time0() - t) * 1000) 10 | 11 | 12 | benchmarks = [] 13 | 14 | 15 | def register(name: str, f: Callable) -> None: 16 | global benchmarks 17 | benchmarks.append((name, f)) 18 | 19 | 20 | def reset() -> None: 21 | benchmarks.clear() 22 | 23 | 24 | def all(desc: str = '') -> None: 25 | print("-" * 20) 26 | print("Running...", desc) 27 | t = 0 28 | for (name, f) in benchmarks: 29 | s = time(f) 30 | t += s 31 | print(name, s, "ms") 32 | print("Total: ", t, "ms") 33 | -------------------------------------------------------------------------------- /packages/jpython/bench/call.py: -------------------------------------------------------------------------------- 1 | from bench import register, all 2 | 3 | 4 | # Test speed of basic function call 5 | def basic_function_call(n=10**6): 6 | def cardinality(n): 7 | return n 8 | 9 | for i in range(n): 10 | cardinality(i) 11 | 12 | 13 | register("basic function call", basic_function_call) 14 | 15 | 16 | # Test speed of object __call__ 17 | def object_function_call(n=10**6): 18 | class IntegerRing: 19 | def __call__(self, n): 20 | return n 21 | 22 | ZZ = IntegerRing() 23 | for i in range(n): 24 | ZZ(i) 25 | 26 | 27 | register("dunder __call__ function call", object_function_call) 28 | 29 | if __name__ == '__main__': 30 | all() 31 | -------------------------------------------------------------------------------- /packages/jpython/bench/fib.py: -------------------------------------------------------------------------------- 1 | # Various ways to compute Fibonacci numbers go here. 2 | 3 | from bench import register, all 4 | 5 | 6 | def rfib(n=30): 7 | if n == 1 or n == 0: 8 | return 1 9 | return rfib(n - 1) + rfib(n - 2) 10 | 11 | 12 | register("recursive fibonacci", rfib) 13 | 14 | if __name__ == '__main__': 15 | all() 16 | -------------------------------------------------------------------------------- /packages/jpython/bench/lambda_.py: -------------------------------------------------------------------------------- 1 | from bench import register, all 2 | 3 | 4 | # Test speed of lambda 5 | def speed_test(n=10**6): 6 | f = lambda a, b: a + b 7 | for i in range(n): 8 | f(2, 3) 9 | 10 | 11 | register("lambda speed test (1)", speed_test) 12 | 13 | 14 | # Test speed of lambda 15 | def speed_test_2(n=10**6): 16 | f = lambda a, b=10, **kwds: a + b 17 | for i in range(n): 18 | f(2) 19 | 20 | 21 | register("lambda speed test (2)", speed_test_2) 22 | 23 | if __name__ == '__main__': 24 | all() 25 | -------------------------------------------------------------------------------- /packages/jpython/bench/mandel.py: -------------------------------------------------------------------------------- 1 | # Benchmark based on what is discussed here: https://news.ycombinator.com/item?id=28814141 2 | 3 | from bench import register, all 4 | 5 | try: 6 | from __python__ import xxx 7 | # jpython 8 | complex = require('@jsage/lib/complex/complex').ComplexNumber 9 | except: 10 | pass 11 | 12 | 13 | # Want this to run on any Python without numpy. 14 | def arange(a, b, step): 15 | eps = 0.00000001 16 | v = [] 17 | x = a 18 | while x + eps < b: 19 | v.append(x) 20 | x += step 21 | return v 22 | 23 | 24 | def mandelbrot_iter(c): 25 | z = complex(0, 0) 26 | for iters in range(200): 27 | if abs(z) >= 2: 28 | return iters 29 | z = z * z + c 30 | return iters 31 | 32 | 33 | def mandelbrot(size=200): 34 | pix = list(range(size * size)) 35 | xPts = arange(-1.5, 0.5, 2.0 / size) 36 | yPts = arange(-1, 1, 2.0 / size) 37 | 38 | for xx, x in enumerate(xPts): 39 | for yy, y in enumerate(yPts): 40 | pix[xx + size * yy] = mandelbrot_iter(complex(x, y)) 41 | return pix 42 | 43 | 44 | register('mandelbrot', mandelbrot) 45 | 46 | # quick consistency check: 47 | assert mandelbrot(5) == [ 48 | 2, 3, 3, 5, 4, 3, 4, 6, 199, 199, 5, 199, 199, 199, 199, 5, 199, 199, 199, 49 | 199, 3, 4, 6, 199, 199 50 | ] 51 | 52 | if __name__ == '__main__': 53 | all() 54 | -------------------------------------------------------------------------------- /packages/jpython/bench/misc.py: -------------------------------------------------------------------------------- 1 | from bench import register, all 2 | 3 | 4 | # JPython is really bad at these, since Javascript 5 | # doesn't really have a notion of generic lists. 6 | def list_times_number(n=100): 7 | for i in range(n): 8 | [0] * 100000 9 | 10 | 11 | register('list_times_number', list_times_number) 12 | 13 | 14 | def list_times_number2(n=1000000): 15 | v = [1, 2, list(range(100))] 16 | len(v * n) 17 | 18 | 19 | register('list_times_number2', list_times_number2) 20 | 21 | 22 | def list_times_number3(n=1000000): 23 | w = [0] 24 | for i in range(n): 25 | w * 3 26 | 27 | 28 | register('list_times_number3', list_times_number3) 29 | 30 | 31 | def list_to_string(n=10**5): 32 | len(str(list(range(n)))) 33 | 34 | 35 | register("list_to_string", list_to_string) 36 | 37 | if __name__ == '__main__': 38 | all() 39 | -------------------------------------------------------------------------------- /packages/jpython/bench/nt.py: -------------------------------------------------------------------------------- 1 | # mypy 2 | # A little pure python number theory library, which is useful 3 | # as a foundation for some microbenchmarks 4 | 5 | from math import sqrt 6 | from typing import Tuple 7 | 8 | 9 | def gcd(a: int, b: int) -> int: 10 | while b: 11 | c = a % b 12 | a = b 13 | b = c 14 | return a 15 | 16 | 17 | def xgcd(a: int, b: int) -> Tuple[int, int, int]: 18 | prevx, x = 1, 0 19 | prevy, y = 0, 1 20 | while b: 21 | q, r = divmod(a, b) 22 | x, prevx = prevx - q * x, x 23 | y, prevy = prevy - q * y, y 24 | a, b = b, r 25 | return a, prevx, prevy 26 | 27 | 28 | def inverse_mod(a: int, N: int) -> int: 29 | """ 30 | Compute multiplicative inverse of a modulo N. 31 | """ 32 | if a == 1 or N <= 1: # common special cases 33 | return a % N 34 | [g, s, _] = xgcd(a, N) 35 | if g != 1: 36 | raise ZeroDivisionError 37 | b = s % N 38 | if b < 0: 39 | b += N 40 | return b 41 | 42 | 43 | def trial_division(N: int, bound: int = 0, start: int = 2) -> int: 44 | if N <= 1: 45 | return N 46 | m = 7 47 | i = 1 48 | dif = [6, 4, 2, 4, 2, 4, 6, 2] 49 | if start > 7: 50 | m = start % 30 51 | if m <= 1: 52 | i = 0 53 | m = start + (1 - m) 54 | elif m <= 7: 55 | i = 1 56 | m = start + (7 - m) 57 | elif m <= 11: 58 | i = 2 59 | m = start + (11 - m) 60 | elif m <= 13: 61 | i = 3 62 | m = start + (13 - m) 63 | elif m <= 17: 64 | i = 4 65 | m = start + (17 - m) 66 | elif m <= 19: 67 | i = 5 68 | m = start + (19 - m) 69 | elif m <= 23: 70 | i = 6 71 | m = start + (23 - m) 72 | elif m <= 29: 73 | i = 7 74 | m = start + (29 - m) 75 | if start <= 2 and N % 2 == 0: 76 | return 2 77 | if start <= 3 and N % 3 == 0: 78 | return 3 79 | if start <= 5 and N % 5 == 0: 80 | return 5 81 | limit = round(sqrt(N)) 82 | if bound != 0 and bound < limit: 83 | limit = bound 84 | while m <= limit: 85 | if N % m == 0: 86 | return m 87 | m += dif[i % 8] 88 | i += 1 89 | 90 | return N 91 | 92 | 93 | def is_prime(N: int) -> bool: 94 | return N > 1 and trial_division(N) == N 95 | 96 | 97 | def pi(n: int = 100000) -> int: 98 | s = 0 99 | for i in range(1, n + 1): 100 | if is_prime(i): 101 | s += 1 102 | return s 103 | -------------------------------------------------------------------------------- /packages/jpython/bench/numbers.py: -------------------------------------------------------------------------------- 1 | from bench import register, all 2 | 3 | from nt import gcd, xgcd, inverse_mod, pi 4 | 5 | 6 | def test_pi(n=100000): 7 | assert pi(n) == 9592 8 | 9 | 10 | register("pi(10**5)", pi) 11 | 12 | 13 | def operator_add(n=100000): 14 | class A: 15 | def __init__(self, i): 16 | self.i = i 17 | 18 | def __add__(self, right): 19 | return A(self.i + right.i) 20 | 21 | a = A(2) 22 | b = A(3) 23 | for i in range(n): 24 | c = a + b 25 | assert c.i == 5 26 | 27 | 28 | register("operator_add", operator_add) 29 | 30 | 31 | def bench_gcd(n=10**5): 32 | s = 0 33 | for i in range(n): 34 | s += gcd(92250, 922350 + i) 35 | assert s == 2414484 36 | return s 37 | 38 | 39 | register("gcd", bench_gcd) 40 | 41 | 42 | def bench_xgcd(n=10**5): 43 | s = 0 44 | for i in range(n): 45 | s += xgcd(92250, 922350 + i)[0] 46 | assert s == 2414484 47 | return s 48 | 49 | 50 | register("xgcd", bench_xgcd) 51 | 52 | 53 | def bench_inverse_mod(n=10**5): 54 | s = 0 55 | for i in range(1, n): 56 | s += inverse_mod(i, 1073741827) # nextprime(2^30) 57 | assert s == 53532319533988 58 | 59 | 60 | register("bench_inverse_mod", bench_inverse_mod) 61 | 62 | 63 | def sum_loop(n=1000000): 64 | s = 0 65 | for i in range(0, n, 3): 66 | s += 1 67 | assert s == 333334 68 | return s 69 | 70 | 71 | register("sum_loop", sum_loop) 72 | 73 | 74 | def sum_range(n=1000000): 75 | n = sum(range(0, n, 3)) 76 | assert n == 166666833333 77 | 78 | 79 | register("sum_range", sum_range) 80 | 81 | 82 | def sum_reversed(n=1000000): 83 | n = sum(reversed(list(range(0, n, 3)))) 84 | assert n == 166666833333 85 | 86 | 87 | register("sum_reversed", sum_reversed) 88 | 89 | if __name__ == '__main__': 90 | all('numbers') 91 | -------------------------------------------------------------------------------- /packages/jpython/bench/p1list.py: -------------------------------------------------------------------------------- 1 | from bench import register, all 2 | from nt import gcd, xgcd, inverse_mod 3 | 4 | 5 | def p1_normalize(N, u, v, compute_s=False): 6 | if N == 1: 7 | return [0, 0, 1] 8 | 9 | u = u % N 10 | v = v % N 11 | if u < 0: u = u + N 12 | if v < 0: v = v + N 13 | if u == 0: 14 | return [0, 1 if gcd(v, N) == 1 else 0, v] 15 | 16 | [g, s, t] = xgcd(u, N) 17 | s = s % N 18 | if s < 0: s = s + N 19 | if gcd(g, v) != 1: 20 | return [0, 0, 0] 21 | 22 | # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N 23 | # Adjust s modulo N/g so it is coprime to N. 24 | if g != 1: 25 | d = N // g 26 | while gcd(s, N) != 1: 27 | s = (s + d) % N 28 | 29 | # Multiply [u,v] by s; then [s*u,s*v] = [g,s*v] (mod N) 30 | u = g 31 | v = (s * v) % N 32 | 33 | min_v = v 34 | min_t = 1 35 | if g != 1: 36 | Ng = N // g 37 | vNg = (v * Ng) % N 38 | t = 1 39 | for k in range(2, g + 1): 40 | v = (v + vNg) % N 41 | t = (t + Ng) % N 42 | if v < min_v and gcd(t, N) == 1: 43 | min_v = v 44 | min_t = t 45 | v = min_v 46 | if u < 0: u = u + N 47 | if v < 0: v = v + N 48 | if compute_s: 49 | s = inverse_mod(s * min_t, N) 50 | else: 51 | s = 0 52 | return [u, v, s] 53 | 54 | 55 | def p1_normalize_many_times(n=10**5): 56 | s = 0 57 | for a in range(n): 58 | s += p1_normalize(46100, 39949, a)[0] 59 | return s 60 | 61 | 62 | register("p1_normalize_many_times", p1_normalize_many_times) 63 | 64 | if __name__ == '__main__': 65 | all('p1list') 66 | -------------------------------------------------------------------------------- /packages/jpython/bench/uuid_.py: -------------------------------------------------------------------------------- 1 | # Benchmark computing uuid's 2 | 3 | # Strangely, pypy is really bad on this benchmark compared to python and jpython. 4 | 5 | from bench import register, all 6 | 7 | from uuid import uuid4 8 | 9 | 10 | def compute_uuids(n=10**5): 11 | for i in range(n): 12 | uuid4() 13 | 14 | 15 | register('compute_uuids', compute_uuids) 16 | 17 | if __name__ == '__main__': 18 | all() 19 | -------------------------------------------------------------------------------- /packages/jpython/bin/jpython: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | function load(mod) { 6 | return require('../dist/tools/' + mod); 7 | } 8 | 9 | var utils = load('utils'); 10 | 11 | // We need ES 6 generators so relaunch with the --harmony flag 12 | if (!utils.generators_available()) { 13 | if (process.execArgv.indexOf('--harmony') != -1) { 14 | console.error('JPython needs ES 6 generators; update your version of nodejs'); 15 | process.exit(1); 16 | } 17 | var args = ['--harmony', module.filename].concat(process.argv.slice(2)); 18 | require('child_process').spawn(process.execPath, args, {stdio:'inherit'}).on('exit', function(code, signal) { 19 | process.exit(code); 20 | }); 21 | } else { 22 | 23 | var path = require('path'); 24 | 25 | var argv = load('cli').argv; 26 | 27 | var base_path = path.normalize(path.join(path.dirname(module.filename), "..")); 28 | var src_path = path.join(base_path, 'src'); 29 | var lib_path = path.join(base_path, 'dev'); 30 | if (!utils.pathExists(path.join(lib_path, 'compiler.js'))) lib_path = path.join(base_path, 'release'); 31 | 32 | if (argv.mode === 'self') { 33 | if (argv.files.length > 0) { 34 | console.error("WARN: Ignoring input files since --self was passed"); 35 | } 36 | load('self')(base_path, src_path, lib_path, argv.complete, argv.profile); 37 | if (argv.test) { 38 | console.log('\nRunning test suite...\n'); 39 | argv.files = []; // Ensure all tests are run 40 | load('test').default(argv, base_path, src_path, path.join(base_path, 'dev')); 41 | } 42 | process.exit(0); 43 | } else 44 | if (argv.mode === 'test') { 45 | load('test').default(argv, base_path, src_path, lib_path); 46 | } else 47 | if (argv.mode === 'lint') { 48 | load('lint').cli(argv, base_path, src_path, lib_path); 49 | } else 50 | if (argv.mode === 'repl') { 51 | load('repl').default({show_js:!argv.no_js, jsage:argv.jsage, tokens:argv.tokens}); 52 | } else 53 | if (argv.mode === 'msgfmt') { 54 | load('msgfmt').cli(argv, base_path, src_path, lib_path); 55 | } else 56 | if (argv.mode === 'compile') { 57 | load('compile').default({argv, src_path, lib_path}); 58 | } else { 59 | throw Error(`unknown mode "${argv.mode}"`); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/jpython/bin/jsage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 3 | 4 | "$SCRIPT_DIR"/jpython repl --jsage -------------------------------------------------------------------------------- /packages/jpython/dev/signatures.json: -------------------------------------------------------------------------------- 1 | { 2 | "ast_types": "4883f312ab1805cdf15c4a4426e7604924629c9d", 3 | "baselib/builtins": "1e85a11549daf8116f88f9ae27a4709df9448ae0", 4 | "baselib/containers": "7a5d8e527c23b97fc3f8a562a6ad4af06eedff50", 5 | "baselib/errors": "f2423e1e2489bdd7fb8c8725bb76436898c8008c", 6 | "baselib/internal": "1b77d40970a8ef2d886132dc7c21c9510d2eb4cd", 7 | "baselib/itertools": "fa1d85ab9bd973495dfaa3bbfa755c6412824fe2", 8 | "baselib/str": "bd40a6a61254ff2809cb633ca290274b702c636b", 9 | "compiler": "d408fae8510a4ff99a80a8dcc9e0966bf0740aed", 10 | "errors": "de1f5055f59b9f4c0289c02f072b622a1db913cf", 11 | "output/__init__": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 12 | "output/classes": "8242f96a7842b0cf44d797a8890174fbfa97a868", 13 | "output/codegen": "9a53fc774165127a6fe5a9441a9a8ddbf21dd259", 14 | "output/comments": "ce9875bcc5cf1a022bb418fa93310e8436eb710d", 15 | "output/exceptions": "008e4763c7716cb36810c42177a6e54397e277b1", 16 | "output/functions": "e5709befcd9acc8a0b318f659a8671cac858bde3", 17 | "output/literals": "d93c46901d6bcd55b2bddaeac20b3b1d23c88800", 18 | "output/loops": "2429ccfb4051b757f7e1b84952029057a45fb009", 19 | "output/modules": "8a7473eb758b7830b2988df51011309b40f41599", 20 | "output/operators": "261a68a5af05e4e79f239d0014ff073dd3b532f5", 21 | "output/statements": "020f5371634d5e2e65d40ed14bde33ccc95e81ef", 22 | "output/stream": "e31995c1b5ff62d3e5cd37f291fa44e12502b660", 23 | "output/utils": "46247a84136206db8eb930d2d0643f03b23afd36", 24 | "parse": "fce20df5bccb8a3db4b151f8391a9db424735fbf", 25 | "string_interpolation": "3c721df7d05d93d7aba570a5da6905fa986df12e", 26 | "tokenizer": "1fb4b780b5a73a72f20ad0b6b4103b7ec8d28732", 27 | "unicode_aliases": "8a7bd6e60facae2bf98963044b6d103d4bd2057f", 28 | "utils": "4140bdceb906d4eff541bd1e4fc7aac6d5fca753", 29 | "#compiler#": "b9225a1456390e06c960c33b131a522719ca7fba", 30 | "#compiled_with#": "b9225a1456390e06c960c33b131a522719ca7fba" 31 | } -------------------------------------------------------------------------------- /packages/jpython/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsage/jpython", 3 | "description": "A Python implementation in Javascript for use by JSage", 4 | "homepage": "https://github.com/sagemathinc/JSage/tree/main/packages/jpython", 5 | "keywords": ["javascript", "python", "language", "compiler"], 6 | "main": "tools/compiler.js", 7 | "scripts": { 8 | "test": "npx tsc && ./bin/jpython self --complete --test && cd bench && ../bin/jpython all.py", 9 | "start": "node bin/jpython", 10 | "build": "npx tsc && node bin/jpython self --complete", 11 | "tsc": "npx tsc -w", 12 | "clean": "rm -rf dist", 13 | "format": "npm run yapf && npm run prettier", 14 | "mypy": "git grep -l '^# mypy' | xargs mypy", 15 | "prettier": "prettier --write .", 16 | "yapf": "yapf -p -i --recursive src/output bench src/compiler.py src/parse.py src/utils.py src/tokenizer.py" 17 | }, 18 | "version": "1.12.3", 19 | "license": "BSD-2-Clause", 20 | "engines": { 21 | "node": ">=0.14.0" 22 | }, 23 | "maintainers": [ 24 | { 25 | "name": "William Stein", 26 | "email": "wstein@sagemath.com" 27 | } 28 | ], 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/sagemathinc/JSage" 32 | }, 33 | "workspaces": ["../../lib"], 34 | "dependencies": { 35 | "@jsage/lib": "^1.11.0" 36 | }, 37 | "devDependencies": { 38 | "@types/node": "^16.7.10", 39 | "prettier": "^2.3.2", 40 | "typescript": "^4.4.3" 41 | }, 42 | "bin": { 43 | "jpython": "bin/jpython", 44 | "jsage": "bin/jsage" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/jpython/release/signatures.json: -------------------------------------------------------------------------------- 1 | { 2 | "ast": "81fefe5999ac0ce23d87f05861a113f21e776714", 3 | "baselib/builtins": "0f1b175e4ac89e14f7b2391fb44483527c60c7d4", 4 | "baselib/containers": "5ec883622a5e689524815ba702950d6cea7a55b1", 5 | "baselib/errors": "f2423e1e2489bdd7fb8c8725bb76436898c8008c", 6 | "baselib/internal": "1b77d40970a8ef2d886132dc7c21c9510d2eb4cd", 7 | "baselib/itertools": "ab1f3257642c8d26fab1ff9d392814a459a60cdb", 8 | "baselib/str": "8c396e4fae02ae34b6d3d805becf7bfdd36625ee", 9 | "compiler": "cfbfceb0439fed3302f78d80b303dca713b25e7b", 10 | "errors": "d1b33848d19b141e19cc079cf78421f29b6d2c05", 11 | "output/__init__": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 12 | "output/classes": "d05c1992cc91b337a4e4ff137b0aa3d626e6624b", 13 | "output/codegen": "8d0b60a8194a947f12f7254e72b0b52b7cd0c887", 14 | "output/comments": "78ec38d8b34da96c078653e2d0d4049d7003906b", 15 | "output/exceptions": "c7d90155694c7108e94d0940a160ae83725421d7", 16 | "output/functions": "2e7368a20261b624e04ddc8a7d5e1e043b0cafff", 17 | "output/literals": "fdbfb744fec8dfea1e1c62cb49767c60160fd402", 18 | "output/loops": "0c438a716523d320b48aba6f8e44904d4ca6bc85", 19 | "output/modules": "5de3ba264f814bf5e62dafb0a53ef84d8538fc28", 20 | "output/operators": "940c4cd439afd9d8236696b58df9f7a2273d0dae", 21 | "output/statements": "7d8dfdc0b43e3e430be7ce3b23ffaf1b41408d8c", 22 | "output/stream": "8fe2bd2f0ddf61b95e26055b9ef18d651b0993ff", 23 | "output/utils": "595968f96f6fdcc51eb41c34d64c92bb595e3cb1", 24 | "parse": "09d1dcb45e5f4d63cbbc45182f87a2f9a54904c4", 25 | "string_interpolation": "bff1cc76d772d24d35707f6c794f38734ca08376", 26 | "tokenizer": "00fc24a41edac9f9b31735e6290348005a623cef", 27 | "unicode_aliases": "79ac6eaa5e6be44a5397d62c561f854a8fe7528e", 28 | "utils": "e8867f026eda170344cd91fa95e9f13ce3e7582a", 29 | "#compiler#": "892b71a294d5ce64430bea89eac5762a41d6c370", 30 | "#compiled_with#": "892b71a294d5ce64430bea89eac5762a41d6c370" 31 | } -------------------------------------------------------------------------------- /packages/jpython/src/baselib/errors.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | # globals: ρσ_str 5 | 6 | NameError = ReferenceError 7 | 8 | class Exception(Error): 9 | 10 | def __init__(self, message): 11 | self.message = message 12 | self.stack = Error().stack 13 | self.name = self.constructor.name 14 | 15 | def __repr__(self): 16 | return self.name + ': ' + self.message 17 | 18 | class AttributeError(Exception): 19 | pass 20 | 21 | class IndexError(Exception): 22 | pass 23 | 24 | class KeyError(Exception): 25 | pass 26 | 27 | class ValueError(Exception): 28 | pass 29 | 30 | class UnicodeDecodeError(Exception): 31 | pass 32 | 33 | class AssertionError(Exception): 34 | pass 35 | 36 | class ZeroDivisionError(Exception): 37 | pass 38 | -------------------------------------------------------------------------------- /packages/jpython/src/baselib/itertools.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | 5 | # globals: ρσ_iterator_symbol, ρσ_bool 6 | 7 | def sum(iterable, start=0): 8 | if Array.isArray(iterable): 9 | def add(prev, cur): 10 | return prev + cur 11 | return iterable.reduce(add, start) 12 | ans = start 13 | iterator = iter(iterable) 14 | r = iterator.next() 15 | while not r.done: 16 | ans += r.value 17 | r = iterator.next() 18 | return ans 19 | 20 | def map(): 21 | iterators = new Array(arguments.length - 1) 22 | func = arguments[0] # noqa: unused-local 23 | args = new Array(arguments.length - 1) # noqa: unused-local 24 | for v'var i = 1; i < arguments.length; i++': 25 | iterators[i - 1] = iter(arguments[i]) # noqa:undef 26 | ans = v"{'_func':func, '_iterators':iterators, '_args':args}" 27 | ans[ρσ_iterator_symbol] = def(): 28 | return this 29 | ans['next'] = def(): 30 | for v'var i = 0; i < this._iterators.length; i++': 31 | r = this._iterators[i].next() 32 | if r.done: 33 | return v"{'done':true}" 34 | this._args[i] = r.value # noqa:undef 35 | return v"{'done':false, 'value':this._func.apply(undefined, this._args)}" 36 | return ans 37 | 38 | def filter(func_or_none, iterable): 39 | func = ρσ_bool if func_or_none is None else func_or_none # noqa: unused-local 40 | ans = v"{'_func':func, '_iterator':ρσ_iter(iterable)}" 41 | ans[ρσ_iterator_symbol] = def(): 42 | return this 43 | ans['next'] = def(): 44 | r = this._iterator.next() 45 | while not r.done: 46 | if this._func(r.value): 47 | return r 48 | r = this._iterator.next() 49 | return v"{'done':true}" 50 | return ans 51 | 52 | def zip(): 53 | iterators = new Array(arguments.length) 54 | for v'var i = 0; i < arguments.length; i++': 55 | iterators[i] = iter(arguments[i]) # noqa:undef 56 | ans = v"{'_iterators':iterators}" 57 | ans[ρσ_iterator_symbol] = def(): 58 | return this 59 | ans['next'] = def(): 60 | args = new Array(this._iterators.length) 61 | for v'var i = 0; i < this._iterators.length; i++': 62 | r = this._iterators[i].next() 63 | if r.done: 64 | return v"{'done':true}" 65 | args[i] = r.value # noqa:undef 66 | return v"{'done':false, 'value':args}" 67 | return ans 68 | 69 | def any(iterable): 70 | for i in iterable: 71 | if i: 72 | return True 73 | return False 74 | 75 | def all(iterable): 76 | for i in iterable: 77 | if not i: 78 | return False 79 | return True 80 | -------------------------------------------------------------------------------- /packages/jpython/src/compiler.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | # globals: console 5 | 6 | from utils import DefaultsError, string_template 7 | from errors import ImportError, SyntaxError 8 | from tokenizer import ALL_KEYWORDS, IDENTIFIER_PAT, tokenizer 9 | from parse import parse, NATIVE_CLASSES, compile_time_decorators 10 | from output.stream import OutputStream 11 | from output.codegen import generate_code 12 | 13 | generate_code() # create the print methods on the AST nodes 14 | 15 | # The following allows this module to be used from a pure javascript, require() 16 | # based environment like Node.js 17 | if jstype(exports) is 'object': 18 | exports.DefaultsError = DefaultsError 19 | exports.parse = parse 20 | exports.compile_time_decorators = compile_time_decorators 21 | exports.OutputStream = OutputStream 22 | exports.string_template = string_template # noqa:undef 23 | # Needed for REPL and linter 24 | exports.ALL_KEYWORDS = ALL_KEYWORDS 25 | exports.IDENTIFIER_PAT = IDENTIFIER_PAT 26 | exports.NATIVE_CLASSES = NATIVE_CLASSES 27 | exports.ImportError = ImportError 28 | exports.SyntaxError = SyntaxError 29 | exports.tokenizer = tokenizer 30 | # Magic! Export all the AST_* nodes 31 | ast = ρσ_modules['ast_types'] 32 | for ast_node in ast: 33 | if ast_node.substr(0, 4) is 'AST_': 34 | exports[ast_node] = ast[ast_node] # noqa:undef 35 | -------------------------------------------------------------------------------- /packages/jpython/src/errors.py: -------------------------------------------------------------------------------- 1 | # mypy 2 | from __python__ import hash_literals, Error # type: ignore 3 | 4 | 5 | class SyntaxError(Error): 6 | def __init__(self, message, filename, line: int, col: int, pos: int, 7 | is_eof: bool): 8 | self.stack = Error().stack 9 | self.message = message 10 | self.line = line 11 | self.col = col 12 | self.pos = pos 13 | self.is_eof = is_eof 14 | self.filename = filename 15 | # The "standard" form for these error attributes 16 | self.lineNumber = line 17 | self.fileName = filename 18 | 19 | def toString(self): 20 | ans = self.message + " (line: " + self.line + ", col: " + self.col + ", pos: " + self.pos + ")" 21 | if self.filename: 22 | ans = self.filename + ':' + ans 23 | if self.stack: 24 | ans += "\n\n" + self.stack 25 | return ans 26 | 27 | 28 | class ImportError(SyntaxError): 29 | pass 30 | 31 | 32 | class EOFError(Error): 33 | pass 34 | 35 | 36 | class RuntimeError(Error): 37 | def __init__(self, message): 38 | self.message = message 39 | 40 | def __call__(self, message): 41 | return RuntimeError(message) 42 | -------------------------------------------------------------------------------- /packages/jpython/src/lib/elementmaker.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: GPL v3 Copyright: 2015, Kovid Goyal 3 | 4 | html_elements = { 5 | 'a', 'abbr', 'acronym', 'address', 'area', 6 | 'article', 'aside', 'audio', 'b', 'base', 'big', 'body', 'blockquote', 'br', 'button', 7 | 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 8 | 'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn', 9 | 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset', 10 | 'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1', 11 | 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'head', 'i', 'iframe', 'img', 'input', 'ins', 12 | 'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter', 13 | 'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option', 14 | 'p', 'pre', 'progress', 'q', 's', 'samp', 'script', 'section', 'select', 15 | 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 16 | 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot', 17 | 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video' 18 | } 19 | 20 | mathml_elements = { 21 | 'maction', 'math', 'merror', 'mfrac', 'mi', 22 | 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 23 | 'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle', 'msub', 24 | 'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 25 | 'munderover', 'none' 26 | } 27 | 28 | svg_elements = { 29 | 'a', 'animate', 'animateColor', 'animateMotion', 30 | 'animateTransform', 'clipPath', 'circle', 'defs', 'desc', 'ellipse', 31 | 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern', 32 | 'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 33 | 'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 34 | 'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use' 35 | } 36 | 37 | html5_tags = html_elements.union(mathml_elements).union(svg_elements) 38 | 39 | def _makeelement(tag, *args, **kwargs): 40 | ans = this.createElement(tag) 41 | 42 | for attr in kwargs: 43 | vattr = str.replace(str.rstrip(attr, '_'), '_', '-') 44 | val = kwargs[attr] 45 | if callable(val): 46 | if str.startswith(attr, 'on'): 47 | attr = attr[2:] 48 | ans.addEventListener(attr, val) 49 | elif val is True: 50 | ans.setAttribute(vattr, vattr) 51 | elif jstype(val) is 'string': 52 | ans.setAttribute(vattr, val) 53 | 54 | for arg in args: 55 | if jstype(arg) is 'string': 56 | arg = this.createTextNode(arg) 57 | ans.appendChild(arg) 58 | return ans 59 | 60 | def maker_for_document(document): 61 | # Create an elementmaker to be used with the specified document 62 | E = _makeelement.bind(document) 63 | Object.defineProperties(E, { 64 | tag: { 65 | 'value':_makeelement.bind(document, tag) 66 | } for tag in html5_tags 67 | }) 68 | return E 69 | 70 | if jstype(document) is 'undefined': 71 | E = maker_for_document({ 72 | 'createTextNode': def(value): return value;, 73 | 'createElement': def(name): 74 | return { 75 | 'name':name, 76 | 'children':[], 77 | 'attributes':{}, 78 | 'setAttribute': def(name, val): this.attributes[name] = val;, 79 | 'appendChild': def(child): this.children.push(child);, 80 | } 81 | }) 82 | else: 83 | E = maker_for_document(document) 84 | -------------------------------------------------------------------------------- /packages/jpython/src/lib/js.py: -------------------------------------------------------------------------------- 1 | # Code that is NOT written in valid Python syntax, but can 2 | # be used from valid python code to accomplish various things 3 | # that are needed. 4 | 5 | # This gives us the new operator as a Python function call: 6 | def js_new(f, *args, **kwds): 7 | return new f(*args, **kwds) 8 | 9 | def js_instanceof(obj, cls): 10 | return r"%js obj instanceof cls" -------------------------------------------------------------------------------- /packages/jpython/src/lib/operator.py: -------------------------------------------------------------------------------- 1 | add = __add__ = def(x, y): return x + y 2 | sub = __sub__ = def(x, y): return x - y 3 | mul = __mul__ = def(x, y): return x * y 4 | div = __div__ = def(x, y): return x / y 5 | 6 | lt = __lt__ = def(x, y): return x < y 7 | le = __le__ = def(x, y): return x <= y 8 | eq = __eq__ = def(x, y): return x is y 9 | ne = __ne__ = def(x, y): return x is not y 10 | ge = __ge__ = def(x, y): return x >= y 11 | gt = __gt__ = def(x, y): return x > y 12 | -------------------------------------------------------------------------------- /packages/jpython/src/lib/pythonize.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2016, Kovid Goyal 3 | # globals: ρσ_str 4 | 5 | 6 | def strings(): 7 | string_funcs = set(( 8 | 'capitalize strip lstrip rstrip islower isupper isspace lower upper swapcase' 9 | ' center count endswith startswith find rfind index rindex format join ljust rjust' 10 | ' partition rpartition replace split rsplit splitlines zfill' 11 | ).split(' ')) 12 | 13 | if not arguments.length: 14 | exclude = {'split', 'replace'} 15 | elif arguments[0]: 16 | exclude = Array.prototype.slice.call(arguments) 17 | else: 18 | exclude = None 19 | if exclude: 20 | string_funcs = string_funcs.difference(set(exclude)) 21 | for name in string_funcs: 22 | String.prototype[name] = ρσ_str.prototype[name] 23 | -------------------------------------------------------------------------------- /packages/jpython/src/lib/random.py: -------------------------------------------------------------------------------- 1 | ########################################################### 2 | # JPython Standard Library 3 | # Author: Alexander Tsepkov 4 | # Copyright 2013 Pyjeon Software LLC 5 | # License: Apache License 2.0 6 | # This library is covered under Apache license, so that 7 | # you can distribute it with your JPython applications. 8 | ########################################################### 9 | 10 | # basic implementation of Python's 'random' library 11 | 12 | # JavaScript's Math.random() does not allow seeding its random generator. 13 | # To bypass that, this module implements its own version that can be seeded. 14 | # I decided on RC4 algorithm for this. 15 | 16 | _seed_state = {'key': [], 'key_i': 0, 'key_j': 0} 17 | 18 | 19 | def _get_random_byte(): 20 | _seed_state.key_i = (_seed_state.key_i + 1) % 256 21 | _seed_state.key_j = (_seed_state.key_j + 22 | _seed_state.key[_seed_state.key_i]) % 256 23 | _seed_state.key[_seed_state.key_i], _seed_state.key[_seed_state.key_j] = \ 24 | _seed_state.key[_seed_state.key_j], _seed_state.key[_seed_state.key_i] 25 | return _seed_state.key[(_seed_state.key[_seed_state.key_i] + \ 26 | _seed_state.key[_seed_state.key_j]) % 256] 27 | 28 | 29 | def seed(x=Date().getTime()): 30 | _seed_state.key_i = _seed_state.key_j = 0 31 | if jstype(x) is 'number': 32 | x = x.toString() 33 | elif jstype(x) is not 'string': 34 | raise TypeError("unhashable type: '" + jstype(x) + "'") 35 | for i in range(256): 36 | _seed_state.key[i] = i 37 | j = 0 38 | for i in range(256): 39 | j = (j + _seed_state.key[i] + x.charCodeAt(i % x.length)) % 256 40 | _seed_state.key[i], _seed_state.key[j] = _seed_state.key[ 41 | j], _seed_state.key[i] 42 | 43 | 44 | seed() 45 | 46 | 47 | def random(): 48 | n = 0 49 | m = 1 50 | for i in range(8): 51 | n += _get_random_byte() * m 52 | m *= 256 53 | return n / 0x10000000000000000 54 | 55 | 56 | # unlike the python version, this DOES build a range object, feel free to reimplement 57 | def randrange(): 58 | return choice(range.apply(this, arguments)) 59 | 60 | 61 | def randint(a, b): 62 | return int(random() * (b - a + 1) + a) 63 | 64 | 65 | def uniform(a, b): 66 | return random() * (b - a) + a 67 | 68 | 69 | def choice(seq): 70 | if len(seq) > 0: 71 | return seq[Math.floor(random() * len(seq))] 72 | else: 73 | raise IndexError() 74 | 75 | 76 | # uses Fisher-Yates algorithm to shuffle an array 77 | def shuffle(x, random_f=random): 78 | x = list(x) 79 | for i in range(len(x)): 80 | j = Math.floor(random_f() * (i + 1)) 81 | x[i], x[j] = x[j], x[i] 82 | return x 83 | 84 | 85 | # similar to shuffle, but only shuffles a subset and creates a copy 86 | def sample(population, k): 87 | x = list(population)[:] 88 | for i in range(len(population) - 1, len(population) - k - 1, -1): 89 | j = Math.floor(random() * (i + 1)) 90 | x[i], x[j] = x[j], x[i] 91 | return x[-k:] 92 | -------------------------------------------------------------------------------- /packages/jpython/src/lib/sys.py: -------------------------------------------------------------------------------- 1 | # Placeholder for now -- nothing implemented 2 | 3 | def exit(status=None): 4 | if status is None: 5 | status = 0 6 | elif not isinstance(status, int): 7 | print(status) 8 | status = 1 9 | process.exit(status) 10 | 11 | argv = process.argv -------------------------------------------------------------------------------- /packages/jpython/src/lib/traceback.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2016, Kovid Goyal 3 | # globals: ρσ_str, ρσ_last_exception 4 | 5 | 6 | def _get_internal_traceback(err): 7 | if isinstance(err, Exception) and err.stack: 8 | lines = ρσ_str.splitlines(err.stack) 9 | final_lines = v'[]' 10 | found_sentinel = False 11 | for i, line in enumerate(lines): 12 | sline = ρσ_str.strip(line) 13 | if i is 0: 14 | final_lines.push(line) 15 | continue 16 | if found_sentinel: 17 | final_lines.push(line) 18 | continue 19 | # These two conditions work on desktop Chrome and Firefox to identify the correct 20 | # line in the traceback. 21 | if sline.startsWith('at new ' + err.name) or sline.startsWith(err.name + '@'): 22 | found_sentinel = True 23 | return final_lines.join('\n') 24 | return err and err.stack 25 | 26 | def format_exception(exc, limit): 27 | if jstype(exc) is 'undefined': 28 | exc = ρσ_last_exception 29 | if not isinstance(exc, Error): 30 | if exc and exc.toString: 31 | return [exc.toString()] 32 | return [] 33 | tb = _get_internal_traceback(exc) 34 | if tb: 35 | lines = ρσ_str.splitlines(tb) 36 | e = lines[0] 37 | lines = lines[1:] 38 | if limit: 39 | lines = lines[:limit+1] if limit > 0 else lines[limit:] 40 | lines.reverse() 41 | lines.push(e) 42 | lines.insert(0, 'Traceback (most recent call last):') 43 | return [l+'\n' for l in lines] 44 | return [exc.toString()] 45 | 46 | def format_exc(limit): 47 | return format_exception(ρσ_last_exception, limit).join('') 48 | 49 | def print_exc(limit): 50 | print(format_exc(limit)) 51 | 52 | def format_stack(limit): 53 | stack = Error().stack 54 | if not stack: 55 | return [] 56 | lines = str.splitlines(stack)[2:] 57 | lines.reverse() 58 | if limit: 59 | lines = lines[:limit+1] if limit > 0 else lines[limit:] 60 | return [l + '\n' for l in lines] 61 | 62 | def print_stack(limit): 63 | print(format_stack(limit).join('')) 64 | -------------------------------------------------------------------------------- /packages/jpython/src/lib/uuid.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2017, Kovid Goyal 3 | # globals: crypto 4 | from __python__ import hash_literals 5 | 6 | from encodings import hexlify, urlsafe_b64decode, urlsafe_b64encode 7 | 8 | RFC_4122 = 1 9 | 10 | if jstype(crypto) is 'object' and crypto.getRandomValues: 11 | random_bytes = def (num): 12 | ans = Uint8Array(num or 16) 13 | crypto.getRandomValues(ans) 14 | return ans 15 | else: 16 | random_bytes = def (num): 17 | ans = Uint8Array(num or 16) 18 | for i in range(ans.length): 19 | ans[i] = Math.floor(Math.random() * 256) 20 | return ans 21 | 22 | 23 | def uuid4_bytes(): 24 | data = random_bytes() 25 | data[6] = 0b01000000 | (data[6] & 0b1111) 26 | data[8] = (((data[8] >> 4) & 0b11 | 0b1000) << 4) | (data[8] & 0b1111) 27 | return data 28 | 29 | 30 | def as_str(): 31 | h = this.hex 32 | return h[:8] + '-' + h[8:12] + '-' + h[12:16] + '-' + h[16:20] + '-' + h[20:] 33 | 34 | 35 | def uuid4(): 36 | b = uuid4_bytes() 37 | return { 38 | 'hex': hexlify(b), 39 | 'bytes': b, 40 | 'variant': RFC_4122, 41 | 'version': 4, 42 | '__str__': as_str, 43 | 'toString': as_str, 44 | } 45 | 46 | 47 | def num_to_string(numbers, alphabet, pad_to_length): 48 | ans = v'[]' 49 | alphabet_len = alphabet.length 50 | numbers = Array.prototype.slice.call(numbers) 51 | for v'var i = 0; i < numbers.length - 1; i++': 52 | x = divmod(numbers[i], alphabet_len) 53 | numbers[i] = x[0] 54 | numbers[i+1] += x[1] 55 | for v'var i = 0; i < numbers.length; i++': 56 | number = numbers[i] 57 | while number: 58 | x = divmod(number, alphabet_len) 59 | number = x[0] 60 | ans.push(alphabet[x[1]]) 61 | if pad_to_length and pad_to_length > ans.length: 62 | ans.push(alphabet[0].repeat(pad_to_length - ans.length)) 63 | return ans.join('') 64 | 65 | 66 | def short_uuid(): 67 | # A totally random uuid encoded using only URL and filename safe characters 68 | return urlsafe_b64encode(random_bytes(), '') 69 | 70 | 71 | def short_uuid4(): 72 | # A uuid4 encoded using only URL and filename safe characters 73 | return urlsafe_b64encode(uuid4_bytes(), '') 74 | 75 | 76 | def decode_short_uuid(val): 77 | return urlsafe_b64decode(val + '==') 78 | -------------------------------------------------------------------------------- /packages/jpython/src/output/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagemathinc/JSage/3652e182035146cb5eada44e11875337d44575dd/packages/jpython/src/output/__init__.py -------------------------------------------------------------------------------- /packages/jpython/src/output/comments.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2018, Kovid Goyal 3 | from __python__ import hash_literals 4 | 5 | from ast_types import AST_Exit, is_node_type 6 | 7 | 8 | def output_comments(comments, output, nlb): 9 | for comm in comments: 10 | if comm.type is "comment1": 11 | output.print("//" + comm.value + "\n") 12 | output.indent() 13 | elif comm.type is "comment2": 14 | output.print("/*" + comm.value + "*/") 15 | if nlb: 16 | output.print("\n") 17 | output.indent() 18 | else: 19 | output.space() 20 | 21 | 22 | def print_comments(self, output): 23 | c = output.options.comments 24 | if c: 25 | start = self.start 26 | if start and not start._comments_dumped: 27 | start._comments_dumped = True 28 | comments = start.comments_before 29 | # XXX: ugly fix for https://github.com/mishoo/RapydScript2/issues/112 30 | # if this node is `return` or `throw`, we cannot allow comments before 31 | # the returned or thrown value. 32 | if is_node_type( 33 | self, AST_Exit 34 | ) and self.value and self.value.start.comments_before and self.value.start.comments_before.length > 0: 35 | comments = (comments 36 | or []).concat(self.value.start.comments_before) 37 | self.value.start.comments_before = [] 38 | 39 | if c.test: 40 | comments = comments.filter( 41 | lambda comment: c.test(comment.value)) 42 | elif jstype(c) is "function": 43 | comments = comments.filter(lambda comment: c(self, comment)) 44 | 45 | output_comments(comments, output, start.nlb) 46 | -------------------------------------------------------------------------------- /packages/jpython/src/output/utils.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2016, Kovid Goyal 3 | from __python__ import hash_literals 4 | 5 | from ast_types import AST_BlockStatement, is_node_type 6 | 7 | 8 | def best_of(a): 9 | best = a[0] 10 | len_ = best.length 11 | for i in range(1, a.length): 12 | if a[i].length < len_: 13 | best = a[i] 14 | len_ = best.length 15 | return best 16 | 17 | 18 | def make_num(num): 19 | str_ = num.toString(10) 20 | a = [str_.replace(RegExp(r"^0\."), ".").replace("e+", "e")] 21 | m = None 22 | 23 | if Math.floor(num) is num: 24 | if num >= 0: 25 | a.push( 26 | "0x" + num.toString(16).toLowerCase(), # probably pointless 27 | "0" + num.toString(8)) 28 | else: 29 | a.push( 30 | "-0x" + 31 | (-num).toString(16).toLowerCase(), # probably pointless 32 | "-0" + (-num).toString(8)) 33 | 34 | m = RegExp(r"^(.*?)(0+)$").exec(num) 35 | if m: 36 | a.push(m[1] + "e" + m[2].length) 37 | 38 | else: 39 | m = RegExp(r"^0?\.(0+)(.*)$").exec(num) 40 | if m: 41 | a.push(m[2] + "e-" + (m[1].length + m[2].length), 42 | str_.substr(str_.indexOf("."))) 43 | 44 | return best_of(a) 45 | 46 | 47 | def make_block(stmt, output): 48 | if is_node_type(stmt, AST_BlockStatement): 49 | stmt.print(output) 50 | return 51 | 52 | def print_statement(): 53 | output.indent() 54 | stmt.print(output) 55 | output.newline() 56 | 57 | output.with_block(print_statement) 58 | 59 | 60 | def create_doctring(docstrings): 61 | ans = [] 62 | for ds in docstrings: 63 | ds = str.rstrip(ds.value) 64 | lines = [] 65 | min_leading_whitespace = '' 66 | for line in ds.split(RegExp(r"$", "gm")): 67 | r = RegExp(r"^\s+").exec(line) 68 | leading_whitespace = '' 69 | if r: 70 | leading_whitespace = r[0].replace(RegExp(r"[\n\r]", "g"), 71 | '') if r else '' 72 | line = line[r[0].length:] 73 | if not str.strip(line): 74 | lines.push(["", ""]) 75 | else: 76 | leading_whitespace = leading_whitespace.replace( 77 | RegExp(r"\t", "g"), ' ') 78 | if leading_whitespace and (not min_leading_whitespace 79 | or leading_whitespace.length < 80 | min_leading_whitespace.length): 81 | min_leading_whitespace = leading_whitespace 82 | lines.push([leading_whitespace, line]) 83 | for lw, l in lines: 84 | if min_leading_whitespace: 85 | lw = lw[min_leading_whitespace.length:] 86 | ans.push(lw + l) 87 | ans.push('') 88 | return str.rstrip(ans.join('\n')) 89 | -------------------------------------------------------------------------------- /packages/jpython/src/string_interpolation.py: -------------------------------------------------------------------------------- 1 | from __python__ import hash_literals # type: ignore 2 | 3 | 4 | def quoted_string(x): 5 | return '"' + x.replace(RegExp('\\\\', 'g'), '\\\\').replace( 6 | RegExp('"', 'g'), r'\"').replace(RegExp('\n', 'g'), '\\n') + '"' 7 | 8 | 9 | def render_markup(markup): 10 | pos, key = 0, '' 11 | while pos < markup.length: 12 | ch = markup[pos] 13 | if ch is '!' or ch is ':': 14 | break 15 | key += ch 16 | pos += 1 17 | fmtspec = markup[pos:] 18 | prefix = '' 19 | if key.endsWith('='): 20 | prefix = key 21 | key = key[:-1] 22 | return 'ρσ_str.format("' + prefix + '{' + fmtspec + '}", ' + key + ')' 23 | 24 | 25 | def interpolate(template, raise_error): 26 | pos = in_brace = 0 27 | markup = '' 28 | ans = [""] 29 | while pos < template.length: 30 | ch = template[pos] 31 | if in_brace: 32 | if ch is '{': 33 | in_brace += 1 34 | markup += '{' 35 | elif ch is '}': 36 | in_brace -= 1 37 | if in_brace > 0: 38 | markup += '}' 39 | else: 40 | ans.push(r'%js [markup]') 41 | ans.push('') 42 | else: 43 | markup += ch 44 | else: 45 | if ch is '{': 46 | if template[pos + 1] is '{': 47 | pos += 1 48 | ans[-1] += '{' 49 | else: 50 | in_brace = 1 51 | markup = '' 52 | elif ch is '}': 53 | if template[pos + 1] is '}': 54 | pos += 1 55 | ans[-1] += '}' 56 | else: 57 | raise_error("f-string: single '}' is not allowed") 58 | else: 59 | ans[-1] += ch 60 | 61 | pos += 1 62 | 63 | if in_brace: 64 | raise_error("expected '}' before end of string") 65 | 66 | if ans[-1] is '+': 67 | ans[-1] = '' 68 | for i in range(len(ans)): 69 | if jstype(ans[i]) is 'string': 70 | ans[i] = quoted_string(ans[i]) 71 | else: 72 | ans[i] = '+' + render_markup.apply(this, ans[i]) + '+' 73 | return ans.join('') 74 | -------------------------------------------------------------------------------- /packages/jpython/src/utils.py: -------------------------------------------------------------------------------- 1 | # mypy 2 | from __python__ import hash_literals, Object # type: ignore 3 | 4 | from typing import Any, Callable, Literal, Optional, Union 5 | 6 | 7 | def array_to_hash(a: list[str]) -> dict[str, bool]: 8 | ret = {} 9 | for i in range(len(a)): 10 | ret[a[i]] = True 11 | return ret 12 | 13 | 14 | def characters(str_: str) -> list[str]: 15 | return str_.split("") 16 | 17 | 18 | def repeat_string(str_: str, i: int) -> str: 19 | if i <= 0: 20 | return "" 21 | if i is 1: 22 | return str_ 23 | d = repeat_string(str_, i >> 1) 24 | d += d 25 | if i & 1: 26 | d += str_ 27 | return d 28 | 29 | 30 | class DefaultsError(ValueError): 31 | def __init__(self, name: str, defs: dict): 32 | ValueError.__init__( 33 | self, 34 | name + ' is not a supported option. Supported options are: ' + 35 | str(Object.keys(defs))) 36 | 37 | 38 | has_prop = Object.prototype.hasOwnProperty.call.bind( 39 | Object.prototype.hasOwnProperty) 40 | 41 | 42 | def defaults(args: Union[Literal[True], dict], defs: dict, croak: Callable): 43 | if args is True: 44 | args = {} 45 | ret = args or {} 46 | if croak: 47 | for i in ret: 48 | if not has_prop(defs, i): 49 | raise DefaultsError(i, defs) 50 | 51 | for i in defs: 52 | ret[i] = args[i] if args and has_prop(args, i) else defs[i] 53 | return ret 54 | 55 | 56 | def merge(obj: dict, ext: dict) -> dict: 57 | for i in ext: 58 | obj[i] = ext[i] 59 | return obj 60 | 61 | 62 | def noop() -> None: 63 | pass 64 | 65 | 66 | def push_uniq(array, el) -> None: 67 | if not array.includes(el): 68 | array.push(el) 69 | 70 | 71 | def string_template(text: str, props: dict) -> str: 72 | def f(str_, p): 73 | return props[p] 74 | 75 | # js replace takes function 76 | return text.replace(r"%js /\{(.+?)\}/g", f) # type: ignore 77 | 78 | 79 | def make_predicate(words: Union[str, list[str]]) -> dict[str, Literal[True]]: 80 | if isinstance(words, str): 81 | words = words.split(" ") 82 | a = Object.create(None) 83 | for k in words: 84 | a[k] = True 85 | return a 86 | 87 | 88 | def cache_file_name(src: str, cache_dir: str) -> Union[None, str]: 89 | if cache_dir: 90 | src = str.replace(src, '\\', '/') 91 | return cache_dir + '/' + str.lstrip( 92 | str.replace(src, '/', '-') + '.json', '-') 93 | return None 94 | 95 | 96 | # This charAt is defined in Javascript and is definitely not s[n], 97 | # so here's a version that will work in pure Python *and* jpython. 98 | def charAt(s: str, n: int) -> str: 99 | try: 100 | return s.charAt(n) # type: ignore 101 | except: 102 | if n < 0 or n >= len(s): return '' 103 | return s[n] 104 | 105 | 106 | # Version of indexOf that works in pure python and jpython 107 | def indexOf(s: str, t: str) -> int: 108 | try: 109 | return s.indexOf(t) # type: ignore 110 | except: 111 | # pure python 112 | try: 113 | return s.index(t) 114 | except: 115 | return -1 116 | 117 | 118 | def startswith(s: str, t: str) -> bool: 119 | try: 120 | # jpython 121 | return s.startsWith(t) # type: ignore 122 | except: 123 | # pure python 124 | return s.startswith(t) 125 | -------------------------------------------------------------------------------- /packages/jpython/test/_import_one.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | nonlocal GLOBAL_SYMBOL 3 | 4 | 'Module level ds1' 5 | 6 | 7 | def toplevel_func(a): 8 | return a + 'toplevel' 9 | 10 | 11 | ''' 12 | Module level ds2 13 | line2 14 | ''' 15 | 16 | 17 | class TopLevel: 18 | def __init__(self, a): 19 | self.a = a 20 | 21 | 22 | 'Module level ds 3' 23 | 24 | 25 | class AClass(TopLevel): 26 | def __init__(self, a): 27 | self.a = a 28 | 29 | 30 | toplevel_var = 'foo' 31 | 32 | if True: 33 | true_var = 'true' 34 | else: 35 | false_var = 'false' 36 | 37 | GLOBAL_SYMBOL = 'i am global' 38 | 39 | from _import_two.other import other 40 | 41 | test_other = other 42 | -------------------------------------------------------------------------------- /packages/jpython/test/_import_two/__init__.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | 3 | 4 | def toplevel_func2(a): 5 | return a + 'toplevel2' 6 | 7 | 8 | class TopLevel2: 9 | def __init__(self, a): 10 | self.a = a 11 | 12 | 13 | toplevel_var2 = 'foo2' 14 | -------------------------------------------------------------------------------- /packages/jpython/test/_import_two/level2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagemathinc/JSage/3652e182035146cb5eada44e11875337d44575dd/packages/jpython/test/_import_two/level2/__init__.py -------------------------------------------------------------------------------- /packages/jpython/test/_import_two/level2/deep.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2016, Kovid Goyal 3 | 4 | deep_var = 'deep' 5 | -------------------------------------------------------------------------------- /packages/jpython/test/_import_two/other.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | 5 | other = 'other' 6 | -------------------------------------------------------------------------------- /packages/jpython/test/_import_two/sub.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | 5 | sub_var = 'sub' 6 | 7 | 8 | def sub_func(): 9 | return sub_var 10 | 11 | 12 | class Sub: 13 | def __init__(self, a): 14 | self.a = a 15 | -------------------------------------------------------------------------------- /packages/jpython/test/annotations.py: -------------------------------------------------------------------------------- 1 | # Annotations are completely disabled by default, so that it is possible to use mypy. 2 | # This turns them back on: 3 | from __python__ import annotations 4 | 5 | def add(a: int, b: float): 6 | return a + b 7 | 8 | assrt.ok(add.__annotations__) 9 | assrt.equal(add.__annotations__['a'], int) 10 | assrt.equal(add.__annotations__['b'], float) 11 | assrt.equal(add.__annotations__['return'], undefined) 12 | 13 | def sum(ls: list) -> int: 14 | pass 15 | 16 | assrt.ok(not (sum.__annotations__ == undefined)) 17 | assrt.deepEqual(sum.__annotations__, { 18 | 'ls': list, 19 | 'return': int 20 | }) 21 | 22 | def optional(a:int=10): 23 | return a 24 | 25 | assrt.ok(not (optional.__annotations__ == undefined)) 26 | assrt.equal(optional.__annotations__.a, int) 27 | assrt.equal(optional.__defaults__.a, 10) 28 | 29 | def otherexpr(a:3+4) -> [1, 2]: 30 | pass 31 | 32 | assrt.ok(not (otherexpr.__annotations__ == undefined)) 33 | assrt.equal(otherexpr.__annotations__['a'], 7) 34 | assrt.deepEqual(otherexpr.__annotations__['return'], [1, 2]) 35 | 36 | def basic(x:float): 37 | pass 38 | 39 | assrt.deepEqual(basic.__annotations__, { 40 | 'x': float 41 | }) 42 | 43 | def kwstarargs(*args:list, **kwargs:dict) -> int: 44 | pass 45 | 46 | assrt.equal(kwstarargs.__annotations__['return'], int) 47 | 48 | def nothing(): 49 | pass 50 | 51 | assrt.ok(nothing.__annotations__ == undefined) 52 | assrt.throws(def(): 53 | nothing.__annotations__['return'] 54 | ) 55 | 56 | test = def(x: int): 57 | pass 58 | 59 | assrt.deepEqual(test.__annotations__, { 60 | 'x': int 61 | }) 62 | 63 | anonreturn = def() -> 'test': 64 | pass 65 | 66 | assrt.equal(anonreturn.__annotations__['return'], 'test') 67 | 68 | assrt.equal(def asexpr(a: int): 69 | a 70 | .__annotations__['a'], int) 71 | 72 | assrt.deepEqual(def(a: int) -> float: 73 | a + 10.0 74 | .__annotations__, { 75 | 'a': int, 76 | 'return': float 77 | }) 78 | 79 | class A: 80 | 81 | def f(self, a : int, b: 'x') -> float: 82 | pass 83 | 84 | assrt.deepEqual(A.prototype.f.__annotations__, {'a':int, 'b':'x', 'return': float}) 85 | -------------------------------------------------------------------------------- /packages/jpython/test/decorators.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2015, Kovid Goyal 3 | 4 | def double(f): 5 | return def (x): 6 | return 2 * f(x) 7 | 8 | def triple(f): 9 | return def(x): 10 | return 3 * f(x) 11 | 12 | @double 13 | def half(x): 14 | return x // 2 15 | 16 | @double 17 | def f1(x): 18 | return x 19 | 20 | @double 21 | @triple 22 | def f2(x): 23 | return x 24 | 25 | def append_one(f): 26 | return def(x): 27 | ans = f(x) 28 | ans.push(1) 29 | return ans 30 | 31 | def append_two(f): 32 | return def(x): 33 | ans = f(x) 34 | ans.push(2) 35 | return ans 36 | 37 | @append_two 38 | @append_one 39 | def f3(): 40 | return [] 41 | 42 | o = {'double':double} 43 | 44 | @o.double 45 | def f4(): 46 | return 1 47 | 48 | assrt.equal(2, half(2)) 49 | assrt.equal(4, f1(2)) 50 | assrt.equal(12, f2(2)) 51 | assrt.equal(2, f4()) 52 | assrt.deepEqual([1, 2], f3()) 53 | 54 | def multiply(amt): 55 | 56 | def wrapper(f): 57 | return def (): 58 | return amt * f() 59 | 60 | return wrapper 61 | 62 | @multiply(2) 63 | def two(): 64 | return 1 65 | 66 | @multiply(3) 67 | def three(): 68 | return 1 69 | 70 | @multiply(2) 71 | @multiply(2) 72 | def four(): 73 | return 1 74 | 75 | assrt.equal(2, two()) 76 | assrt.equal(3, three()) 77 | assrt.equal(4, four()) 78 | -------------------------------------------------------------------------------- /packages/jpython/test/docstrings.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2016, Kovid Goyal 3 | # globals: ρσ_module_doc__ 4 | 5 | import _import_one 6 | 7 | def f(): 8 | " A basic docstring " 9 | pass 10 | 11 | assrt.equal(f.__doc__, 'A basic docstring') 12 | assrt.equal(_import_one.__doc__, 'Module level ds1\n\nModule level ds2\nline2\n\nModule level ds 3') 13 | 14 | def g(): 15 | ''' 16 | A more complex docstring: 17 | xxx 18 | yyyy 19 | 20 | the end 21 | ''' 22 | pass 23 | 24 | assrt.equal(g.__doc__, 'A more complex docstring:\n xxx\n yyyy\n\nthe end') 25 | 26 | class D: 27 | ' Docstring for a class ' 28 | 29 | def method(self): 30 | 'ds for a method' 31 | pass 32 | 33 | assrt.equal(D().__doc__, 'Docstring for a class') 34 | assrt.equal(D().method.__doc__, 'ds for a method') 35 | 36 | x = def(): 37 | 'xxx' 38 | 39 | assrt.equal(x.__doc__, 'xxx') 40 | -------------------------------------------------------------------------------- /packages/jpython/test/elementmaker_.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2015, Kovid Goyal 3 | # globals: assrt 4 | 5 | from elementmaker import E 6 | 7 | eq = assrt.equal 8 | 9 | 10 | def dummy_elem_eq(a, b): 11 | eq(jstype(a), jstype(b)) 12 | if (jstype(a) == 'string'): 13 | eq(a, b) 14 | return 15 | eq(a.attributes.length, b.attributes.length) 16 | eq(a.children.length, b.children.length) 17 | eq(a.name, b.name) 18 | for attr in a.attributes: 19 | eq(a[attr], b[attr]) 20 | for i, child in enumerate(a.children): 21 | dummy_elem_eq(child, b.children[i]) 22 | 23 | 24 | q = E.div('text', id='1', class_='c', data_x='x') 25 | dummy_elem_eq( 26 | q, { 27 | 'name': 'div', 28 | 'children': ['text'], 29 | 'attributes': { 30 | 'id': '1', 31 | 'class': 'c', 32 | 'data-x': 'x' 33 | } 34 | }) 35 | 36 | q = E.div( 37 | E.span('a'), 38 | E.span('b'), 39 | E.a(), 40 | id='1', 41 | boolean_attr=True, 42 | ) 43 | dummy_elem_eq( 44 | q, { 45 | 'name': 46 | 'div', 47 | 'children': [ 48 | { 49 | 'name': 'span', 50 | 'children': ['a'], 51 | 'attributes': {} 52 | }, 53 | { 54 | 'name': 'span', 55 | 'children': ['b'], 56 | 'attributes': {} 57 | }, 58 | { 59 | 'name': 'a', 60 | 'children': [], 61 | 'attributes': {} 62 | }, 63 | ], 64 | 'attributes': { 65 | 'id': '1', 66 | 'boolean-attr': 'boolean-attr' 67 | } 68 | }) 69 | -------------------------------------------------------------------------------- /packages/jpython/test/generators.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | 5 | 6 | def g1(): 7 | yield 1 8 | yield 2 9 | 10 | 11 | def g2(): 12 | for i in range(2): 13 | yield from g1() 14 | 15 | 16 | def g3(): 17 | data = yield 1 18 | yield data 19 | 20 | 21 | class A: 22 | def __init__(self): 23 | self.items = [1, 2, 3] 24 | 25 | def __iter__(self): 26 | for x in self.items: 27 | yield x 28 | 29 | 30 | assrt.deepEqual([x for x in g1()], [1, 2]) 31 | assrt.deepEqual([x for x in g2()], [1, 2, 1, 2]) 32 | assrt.deepEqual([x for x in A()], [1, 2, 3]) 33 | 34 | g = g3() 35 | assrt.equal(g.next().value, 1) 36 | assrt.equal(g.next('a').value, 'a') 37 | 38 | a = (x for x in range(3)) 39 | assrt.deepEqual(list(a), [0, 1, 2]) 40 | a = ([x, x**2] for x in range(3)) 41 | assrt.deepEqual(list(a), [[0, 0], [1, 1], [2, 4]]) 42 | assrt.deepEqual(list(x for x in range(3)), [0, 1, 2]) 43 | 44 | 45 | def t(a, b): 46 | assrt.deepEqual(list(a), list(b)) 47 | 48 | 49 | t((x for x in range(1)), (y for y in range(1))) 50 | -------------------------------------------------------------------------------- /packages/jpython/test/imports.py: -------------------------------------------------------------------------------- 1 | # globals:test_path, GLOBAL_SYMBOL, assrt 2 | from _import_one import toplevel_var, toplevel_func as tf, TopLevel, true_var, false_var, test_other 3 | from _import_two import (toplevel_var2, 4 | toplevel_func2, TopLevel2 as TL2) 5 | 6 | def AClass(x): 7 | return this 8 | 9 | eq = assrt.equal 10 | # Test import of top-level variables and callables 11 | eq(toplevel_var, 'foo') 12 | eq(tf('x'), 'xtoplevel') 13 | eq(toplevel_var2, 'foo2') 14 | eq(toplevel_func2('x'), 'xtoplevel2') 15 | eq(false_var, undefined) 16 | eq(test_other, 'other') 17 | 18 | # Test import of top-level vars in a conditional 19 | eq('true', true_var) 20 | 21 | # Test plain imports 22 | import _import_one 23 | eq(_import_one.toplevel_var, toplevel_var) 24 | eq(_import_one.toplevel_func('x'), tf('x')) 25 | 26 | # Test recognition of imported classes 27 | tl = TopLevel('1') 28 | eq(tl.a, '1') 29 | tl2 = TL2('x') 30 | eq(tl2.a, 'x') 31 | 32 | # Test access to submodules via plain imports 33 | import _import_two.sub, _import_two.sub as ts 34 | eq('sub', _import_two.sub.sub_var) 35 | eq('sub', ts.sub_var) 36 | eq('sub', _import_two.sub.sub_func()) 37 | 38 | # Test deep import 39 | from _import_two.level2.deep import deep_var 40 | eq('deep', deep_var) 41 | 42 | # Test that class accessed via plain import is 43 | # recognized 44 | s = _import_two.sub.Sub(1) 45 | eq(s.a, 1) 46 | s2 = ts.Sub(1) 47 | eq(s2.a, 1) 48 | 49 | 50 | # Test that a class imported into an inner scope is not recognized as a class 51 | # outside that scope 52 | def inner(): 53 | from _import_one import AClass 54 | a = AClass(1) 55 | eq(a.a, 1) 56 | 57 | inner() 58 | b = AClass(1) 59 | eq(b, this) 60 | 61 | # Test global symbol declared in other module 62 | eq(GLOBAL_SYMBOL, 'i am global') 63 | 64 | # Import errors happen during parsing, so we cannot test them directly as they would 65 | # prevent this file from being parsed. 66 | 67 | assrt.throws(def(): 68 | JPython.parse('from _import_one import not_exported', {'basedir':test_path}).body[0] 69 | , /not exported/) 70 | assrt.throws(def(): 71 | JPython.parse('import xxxx', {'basedir':test_path}).body[0] 72 | , /doesn't exist/) 73 | -------------------------------------------------------------------------------- /packages/jpython/test/jsage.py: -------------------------------------------------------------------------------- 1 | # Test jsage language modifications 2 | 3 | # ^ is xor in python by default 4 | assrt.equal(2^3, 1) 5 | assrt.equal(2**3, 8) 6 | # note the precedence, despite how I wrote this! 7 | assrt.equal(2^3 + 1, 2^4) 8 | 9 | from __python__ import exponent 10 | # now ^ is exponent and ^^ is xor 11 | assrt.equal(2^3, 8) 12 | assrt.equal(2^^3, 1) 13 | assrt.equal(2**3, 8) 14 | 15 | # ^ really **is** exponentiation, not xor, since the tokenizer does it. 16 | # This means the precedence is correct (i.e., very high). 17 | assrt.equal(2^3 + 1, 9) 18 | 19 | # note that eval is not changed. Maybe this is bad? 20 | assrt.equal(eval('2^3'), 1) 21 | 22 | 23 | from __python__ import no_exponent 24 | # now ^ is back (and ^^ would be a syntax error - can't test this) 25 | assrt.equal(2^3, 1) 26 | 27 | # Ellipses range parsing 28 | # Enable it: 29 | from __python__ import ellipses 30 | 31 | # With numerical literals 32 | assrt.equal(str([1..5]), 'range(1, 6)') 33 | 34 | # With expressions 35 | a = 2; b = 7 36 | assrt.equal(str([a+a..b+2]), 'range(4, 10)') 37 | 38 | # With a function call 39 | def f(n): 40 | return n+1 41 | assrt.equal(str([f(10)..f(1000)]), 'range(11, 1002)') 42 | 43 | # With a floating point literal 44 | assrt.equal(str([1.5..5]), 'range(1.5, 6)') 45 | 46 | # Numerical literals 47 | from __python__ import numbers 48 | # will parse all numbers as one less! 49 | def Number(s): 50 | return parseFloat(s) - parseFloat('1.0') 51 | assrt.equal(2.5, parseFloat('1.5')) 52 | -------------------------------------------------------------------------------- /packages/jpython/test/lambda_.py: -------------------------------------------------------------------------------- 1 | # No args 2 | nothing = lambda: None 3 | 4 | assert nothing() == None 5 | 6 | # simple functions 7 | 8 | add = lambda a, b: a + b 9 | sub = lambda a, b: a - b 10 | 11 | assert add(1, 2) == 3 12 | assert sub(1, 2) == -1 13 | 14 | # kwargs with defaults 15 | 16 | f = lambda a, b=7, c=10: a + b * c 17 | assert f(1, 2, 3) == 1 + 2 * 3 18 | assert f(1, c=20) == 1 + 7 * 20 19 | assert f(1, b=10) == 1 + 10 * 10 20 | assert f(0) == 7 * 10 21 | 22 | # vargs 23 | f = lambda *args: args 24 | assert list(f(['hello'])) == [['hello']] 25 | 26 | assert list(f('hello', 'world')) == ['hello', 'world'] 27 | 28 | # varkwds 29 | 30 | f = lambda **kwds: kwds 31 | assert f(a=10) == {'a': 10} 32 | 33 | # both 34 | 35 | f = lambda *args, **kwds: [args, kwds] 36 | 37 | v = f('hello', world='there') 38 | assert list(v[0]) == ['hello'] 39 | assert v[1] == {'world': 'there'} 40 | 41 | # Examples from people arguing about Python and lambda on Hacker News today 42 | # 43 | 44 | v = list(filter(lambda x: x < 10, map(lambda x: x * x, [1, 2, 3, 4]))) 45 | assert v == [1, 4, 9] 46 | 47 | assert v == [x * x for x in [1, 2, 3, 4] if x * x < 10] 48 | 49 | 50 | n = (lambda x: x+1 if \ 51 | True \ 52 | else x+x)\ 53 | (10) 54 | assert n == 11 55 | -------------------------------------------------------------------------------- /packages/jpython/test/loops.py: -------------------------------------------------------------------------------- 1 | # globals: assrt 2 | # loop through values, not indices 3 | a = ['foo', 'bar', 'baz'] 4 | for val in a: 5 | assrt.ok(val in a) 6 | 7 | for i in range(len(a)): 8 | assrt.ok(a[i] in a) 9 | 10 | for i, val in enumerate(a): # testing that comments are allowed here 11 | assrt.equal(a[i], val) 12 | 13 | # nesting 14 | final = [] 15 | for i in [1,2]: 16 | for j in [4,5,6]: 17 | final.push((i,j)) 18 | assrt.deepEqual(final, [[1,4], [1,5], [1,6], [2,4], [2,5], [2,6]]) 19 | 20 | i = 0 21 | while i < len(a): 22 | assrt.ok(a[i] in a) 23 | i += 1 24 | 25 | counter = 5 26 | factorial = 1 27 | do: 28 | factorial *= counter 29 | counter -= 1 30 | .while counter > 0 31 | assrt.equal(factorial, 120) 32 | 33 | # for-in 34 | hash = { 35 | "foo": 1, 36 | "bar": 1, 37 | "baz": 1, 38 | } 39 | i = 0 40 | for key in hash: 41 | assrt.equal(key, a[i]) 42 | i += 1 43 | 44 | word = "test" 45 | i = 0 46 | for letter in word: 47 | assrt.equal(letter, word[i]) 48 | i += 1 49 | 50 | for b in (1, 1): 51 | assrt.equal(b, 1) 52 | 53 | for q in range(3): 54 | u = q 55 | assrt.equal(u, q) 56 | for q in range(3): 57 | u = q 58 | q = 10 59 | assrt.equal(u, 2) 60 | a = [1,2] 61 | for li in range(len(a)): 62 | a.pop() 63 | assrt.equal(len(a), 0) 64 | r = range(3) 65 | assrt.deepEqual(list(r), list(r)) 66 | items = [] 67 | for outer in r: 68 | items.push(outer) 69 | for b in r: 70 | items.push(b) 71 | assrt.deepEqual(items, [0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2]) 72 | r = range(3) 73 | if jstype(Proxy) is 'function': 74 | assrt.equal(r[1], 1) 75 | assrt.equal(r[1], 1) 76 | assrt.equal(r[2], 2) 77 | assrt.equal(r.count(0), 1) 78 | assrt.equal(r.count(4), 0) 79 | assrt.equal(r.index(1), 1) 80 | assrt.throws(def(): r.index(4);, ValueError) 81 | 82 | dest = [] 83 | for al in 'a', 'b', 'c': 84 | dest.push(al) 85 | assrt.equal(dest.join(''), 'abc') 86 | -------------------------------------------------------------------------------- /packages/jpython/test/newlines.py: -------------------------------------------------------------------------------- 1 | # Obviously do NOT run autoformatting on this! If you do, it defeats 2 | # the point of all the tests. 3 | 4 | a = 1 + \ 5 | 2 + \ 6 | 3 7 | assert a == 6 8 | 9 | a = (1 + 10 | 2 + 11 | 3 + 12 | 4) 13 | 14 | assert a == 10 15 | 16 | a = (1, 17 | 2, 18 | 3, 19 | 4) 20 | 21 | assert sum(a) == 10 22 | 23 | a = [1, 24 | 2, 25 | 3] 26 | 27 | assert sum(a) == 6 28 | 29 | a = """ 30 | abc""" 31 | 32 | assert a == '\nabc' 33 | 34 | a = ''' 35 | abc''' 36 | 37 | assert a == '\nabc' 38 | 39 | 40 | a = {'x':10, 41 | 'y':15} 42 | 43 | assert str(a) == "{'x': 10, 'y': 15}" 44 | 45 | from math import (sin, 46 | cos, 47 | pi) 48 | assert sin(0) + cos(pi) == -1.0 49 | -------------------------------------------------------------------------------- /packages/jpython/test/random_lib.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sagemathinc/JSage/3652e182035146cb5eada44e11875337d44575dd/packages/jpython/test/random_lib.py -------------------------------------------------------------------------------- /packages/jpython/test/regexp.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD 3 | # Copyright: 2015, Kovid Goyal 4 | 5 | # Test the re module 6 | import re 7 | s = "Isaac Newton, physicist" 8 | c = re.search("(\\w+) (\\w+)", s) 9 | assrt.equal(c.group(1), 'Isaac') 10 | assrt.equal(c.group(2), 'Newton') 11 | c = re.match("(\\w+) (\\w+)", s) 12 | assrt.equal(c.group(1), 'Isaac') 13 | assrt.equal(c.start(1), 0) 14 | assrt.equal(c.end(1), c.group(1).length) 15 | assrt.equal(c.start(2), c.group(1).length + 1) 16 | assrt.equal(c.group(2), 'Newton') 17 | m = re.search('a(b)cd', 'abc abcd') 18 | assrt.equal(m.group(1), 'b') 19 | assrt.equal(m.start(1), m.string.lastIndexOf('b')) 20 | assrt.deepEqual(re.split('\\s', s), ['Isaac', 'Newton,', 'physicist']) 21 | assrt.deepEqual(re.findall('s[a-z]', s), ['sa', 'si', 'st']) 22 | assrt.deepEqual([m.group() for m in re.finditer('s[a-z]', s)], ['sa', 'si', 'st']) 23 | assrt.deepEqual(re.findall(/s[a-z]/, s), ['sa', 'si', 'st']) 24 | assrt.equal(re.sub('[A-Z]', '_', s), '_saac _ewton, physicist') 25 | assrt.equal(re.sub('[A-Z]', '_', s, count=1), '_saac Newton, physicist') 26 | assrt.equal(re.search('a[.]b', 'axb'), None) 27 | assrt.equal(re.search(r'a\.b', 'axb'), None) 28 | assrt.equal(re.search('a[.]b', 'axb', flags=re.D), None) 29 | assrt.equal(re.search('.+', 'a\nb').group(), 'a') 30 | assrt.equal(re.search('.+', 'a\nb', flags=re.D).group(), 'a\nb') 31 | assrt.equal(re.search('(?s).+', 'a\nb').group(), 'a\nb') 32 | assrt.equal(re.sub('a(b)', r'xx', 'ab'), r'xx') 33 | assrt.equal(re.sub('a(b)', r'\\1', 'ab'), r'\1') 34 | assrt.equal(re.sub('a(b)', r'\\\1', 'ab'), r'\b') 35 | assrt.equal(re.sub('a(b)', r'\g<1>', 'ab'), r'b') 36 | assrt.equal(re.sub('a(b)', def(m):return m.group(1);, 'ab'), r'b') 37 | assrt.equal(']', re.match('[]]', ']').group()) 38 | 39 | assrt.throws(def():re.search(r'(?(1)a|b)b', 'ab');, re.error) 40 | 41 | # Test lookbehind assertions 42 | assrt.equal('acdb', re.sub(r'(?<=a)b', 'c', 'abdb')) 43 | 44 | # Test named groups 45 | assrt.equal('aa', re.sub(r'(?Pa)b', r'\g\1', 'ab')) 46 | assrt.equal('bb', re.sub(r'(?Pa)(?P=a)', r'bb', 'aa')) 47 | assrt.equal('ab', re.sub(r'(.)(?Pa)', r'\g\1', 'ba')) 48 | assrt.deepEqual({'a':'a', 'b':'b'}, re.search(r'(?Pa)(?Pb)', 'ab').groupdict()) 49 | 50 | # Test verbose mode literals 51 | assrt.equal(re.search(/// 52 | a 53 | . # anything 54 | b 55 | ///, ' axb').group(), 'axb') 56 | -------------------------------------------------------------------------------- /packages/jpython/test/scoped_flags.py: -------------------------------------------------------------------------------- 1 | # vim:fileencoding=utf-8 2 | # License: BSD Copyright: 2016, Kovid Goyal 3 | 4 | a = {1: 1} 5 | assrt.ok(not isinstance(a, dict)) 6 | 7 | from __python__ import dict_literals, overload_getitem 8 | 9 | a = {1: 1} 10 | assrt.ok(isinstance(a, dict)) 11 | assrt.equal(a[1], 1) 12 | a[2] = 2 13 | assrt.equal(a[2], 2) 14 | assrt.deepEqual(list(a.keys()), [1, 2]) 15 | from __python__ import no_dict_literals, no_overload_getitem 16 | 17 | a = {1: 1} 18 | assrt.ok(not isinstance(a, dict)) 19 | 20 | 21 | def f(): 22 | from __python__ import dict_literals 23 | a = {1: 1} 24 | assrt.ok(isinstance(a, dict)) 25 | 26 | 27 | a = {1: 1} 28 | assrt.ok(not isinstance(a, dict)) 29 | 30 | 31 | class S: 32 | from __python__ import bound_methods 33 | 34 | def __init__(self): 35 | self.a = 3 36 | 37 | def val(self): 38 | return self.a if self else None 39 | 40 | 41 | f = S().val 42 | assrt.equal(f(), S().val()) 43 | 44 | 45 | class U: 46 | def __init__(self): 47 | self.a = 3 48 | 49 | def val(self): 50 | return self.a if self else None 51 | 52 | 53 | f = U().val 54 | assrt.equal(f(), None) 55 | 56 | 57 | class C: 58 | def __init__(self): 59 | self.a = 3 60 | 61 | def uval1(self): 62 | return self.a if self else None 63 | 64 | from __python__ import bound_methods 65 | 66 | def bval(self): 67 | return self.a 68 | 69 | from __python__ import no_bound_methods 70 | 71 | def uval2(self): 72 | return self.a if self else None 73 | 74 | 75 | c = C() 76 | u1, u2 = c.uval1, c.uval2 77 | f = c.bval 78 | assrt.equal(u1(), None) 79 | assrt.equal(u2(), None) 80 | assrt.equal(f(), 3) 81 | -------------------------------------------------------------------------------- /packages/jpython/test/typing_.py: -------------------------------------------------------------------------------- 1 | # mypy 2 | # You can run this file through mypy and it passes the checks. 3 | # You can also use it in jpython. 4 | # 5 | # The way this works is as follows: 6 | # - the "typing" module is mocked by the jpython compiler so that 7 | # importing from it is a no-op (so src/parser.py), and 8 | # - function annotations are not defined, since that potentially 9 | # involves running code defined in typing at runtime, which 10 | # can't work. 11 | 12 | from typing import Iterator 13 | 14 | 15 | def fib(n: int) -> Iterator[int]: 16 | a, b = 0, 1 17 | while a < n: 18 | yield a 19 | a, b = b, a + b 20 | -------------------------------------------------------------------------------- /packages/jpython/tools/compiler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 William Stein 3 | * Copyright (C) 2015 Kovid Goyal 4 | * 5 | * Distributed under terms of the BSD license 6 | */ 7 | 8 | // Thin wrapper around (release|dev)/compiler.js to setup some global facilities and 9 | // export the compiler's symbols safely. 10 | 11 | import { join, relative } from "path"; 12 | import { readFileSync as readfile, writeFileSync as writefile } from "fs"; 13 | import { createContext, runInContext } from "vm"; 14 | import { pathExists, sha1sum } from "./utils"; 15 | 16 | export type Compiler = any; // for now 17 | 18 | interface Options { 19 | console?; 20 | } 21 | 22 | export default function createCompiler(options: Options = {}): Compiler { 23 | const compiler_exports: Compiler = {}; 24 | const compiler_context = createContext({ 25 | console: options.console ?? console, 26 | readfile, 27 | writefile, 28 | sha1sum, 29 | require, 30 | exports: compiler_exports, 31 | }); 32 | 33 | const base = join(__dirname, "..", ".."); 34 | let compiler_dir = join(base, "dev"); 35 | if (!pathExists(join(compiler_dir, "compiler.js"))) { 36 | compiler_dir = join(base, "release"); 37 | } 38 | const compiler_file = join(compiler_dir, "compiler.js"); 39 | const compilerjs = readfile(compiler_file, "utf-8"); 40 | runInContext(compilerjs, compiler_context, relative(base, compiler_file)); 41 | return compiler_exports; 42 | } 43 | -------------------------------------------------------------------------------- /packages/jpython/tools/embedded_compiler.js: -------------------------------------------------------------------------------- 1 | /* vim:fileencoding=utf-8 2 | * 3 | * Copyright (C) 2016 Kovid Goyal 4 | * 5 | * Distributed under terms of the BSD license 6 | */ 7 | "use strict"; /*jshint node:true */ 8 | 9 | var has_prop = Object.prototype.hasOwnProperty.call.bind( 10 | Object.prototype.hasOwnProperty 11 | ); 12 | 13 | module.exports = function (compiler, baselib, runjs, name) { 14 | var LINE_CONTINUATION_CHARS = ":\\"; 15 | runjs = runjs || eval; 16 | runjs(print_ast(compiler.parse(""), true)); 17 | runjs('var __name__ = "' + (name || "__embedded__") + '";'); 18 | 19 | function print_ast( 20 | ast, 21 | keep_baselib, 22 | keep_docstrings, 23 | private_scope, 24 | write_name 25 | ) { 26 | var output_options = { 27 | omit_baselib: !keep_baselib, 28 | write_name: !!write_name, 29 | private_scope: !!private_scope, 30 | beautify: true, 31 | keep_docstrings: keep_docstrings, 32 | }; 33 | if (keep_baselib) output_options.baselib_plain = baselib; 34 | var output = new compiler.OutputStream(output_options); 35 | ast.print(output); 36 | return output.get(); 37 | } 38 | 39 | return { 40 | toplevel: null, 41 | 42 | compile: (code, opts) => { 43 | opts = opts || {}; 44 | var classes = this.toplevel ? this.toplevel.classes : undefined; 45 | var scoped_flags = this.toplevel ? this.toplevel.scoped_flags : undefined; 46 | this.toplevel = compiler.parse(code, { 47 | filename: opts.filename || "", 48 | basedir: "__stdlib__", 49 | classes: classes, 50 | scoped_flags: scoped_flags, 51 | discard_asserts: opts.discard_asserts, 52 | }); 53 | var ans = print_ast( 54 | this.toplevel, 55 | opts.keep_baselib, 56 | opts.keep_docstrings, 57 | opts.private_scope, 58 | opts.write_name 59 | ); 60 | if (classes) { 61 | var class_exports = {}; 62 | var self = this; 63 | this.toplevel.exports.forEach(function (name) { 64 | class_exports[name] = true; 65 | }); 66 | Object.getOwnPropertyNames(classes).forEach(function (name) { 67 | if ( 68 | !has_prop(class_exports, name) && 69 | !has_prop(self.toplevel.classes, name) 70 | ) 71 | self.toplevel.classes[name] = classes[name]; 72 | }); 73 | } 74 | scoped_flags = this.toplevel.scoped_flags; 75 | 76 | return ans; 77 | }, 78 | }; 79 | }; 80 | -------------------------------------------------------------------------------- /packages/jpython/tools/ini.js: -------------------------------------------------------------------------------- 1 | /* vim:fileencoding=utf-8 2 | * 3 | * Copyright (C) 2015 Kovid Goyal 4 | * 5 | * Distributed under terms of the BSD license 6 | */ 7 | "use strict"; /*jshint node:true */ 8 | 9 | var fs = require("fs"); 10 | var path = require("path"); 11 | 12 | function parse_ini_data(data) { 13 | // Based on MIT licensed code from: 14 | // https://github.com/shockie/node-iniparser/blob/master/lib/node-iniparser.js 15 | var ans = {}, 16 | match; 17 | var lines = data.split(/\r\n|\r|\n/); 18 | var section = null; 19 | var section_pat = /^\s*\[\s*([^\]]*)\s*\]\s*$/; 20 | var param_pat = /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/; 21 | var comment_pat = /^\s*;.*$/; 22 | 23 | lines.forEach(function (line) { 24 | if (comment_pat.test(line)) { 25 | return; 26 | } else if (param_pat.test(line)) { 27 | match = line.match(param_pat); 28 | if (section) { 29 | ans[section][match[1]] = match[2]; 30 | } else { 31 | ans[match[1]] = match[2]; 32 | } 33 | } else if (section_pat.test(line)) { 34 | match = line.match(section_pat); 35 | ans[match[1]] = {}; 36 | section = match[1]; 37 | } else if (line.length === 0 && section) { 38 | section = null; 39 | } 40 | }); 41 | return ans; 42 | } 43 | 44 | function find_cfg_file(toplevel_dir) { 45 | var current_dir = toplevel_dir, 46 | previous_dir = toplevel_dir; 47 | do { 48 | try { 49 | return fs.readFileSync(path.join(current_dir, "setup.cfg"), "utf-8"); 50 | } catch (e) { 51 | if (e.code !== "ENOENT") throw e; 52 | } 53 | previous_dir = current_dir; 54 | current_dir = path.dirname(current_dir); 55 | } while (current_dir != previous_dir && current_dir); 56 | 57 | return null; 58 | } 59 | 60 | function read_config(toplevel_dir) { 61 | var data = find_cfg_file(toplevel_dir); 62 | if (!data) return {}; 63 | return parse_ini_data(data); 64 | } 65 | 66 | exports.read_config = read_config; 67 | -------------------------------------------------------------------------------- /packages/jpython/tools/web_repl.js: -------------------------------------------------------------------------------- 1 | /* vim:fileencoding=utf-8 2 | * 3 | * Copyright (C) 2016 Kovid Goyal 4 | * 5 | * Distributed under terms of the BSD license 6 | */ 7 | "use strict"; /*jshint node:true */ 8 | var vm = require("vm"); 9 | var embedded_compiler = require("tools/embedded_compiler.js"); 10 | 11 | module.exports = function (compiler, baselib) { 12 | var ctx = vm.createContext(); 13 | var LINE_CONTINUATION_CHARS = ":\\"; 14 | var find_completions = null; 15 | var streaming_compiler = embedded_compiler( 16 | compiler, 17 | baselib, 18 | function (js) { 19 | return vm.runInContext(js, ctx); 20 | }, 21 | "__repl__" 22 | ); 23 | 24 | return { 25 | in_block_mode: false, 26 | 27 | replace_print: (write_line_func) => { 28 | ctx.print = function () { 29 | var parts = []; 30 | for (var i = 0; i < arguments.length; i++) 31 | parts.push(ctx.ρσ_str(arguments[i])); 32 | write_line_func(parts.join(" ")); 33 | }; 34 | }, 35 | 36 | is_input_complete: (source) => { 37 | if (!source || !source.trim()) return false; 38 | var lines = source.split("\n"); 39 | var last_line = lines[lines.length - 1].trimRight(); 40 | if (this.in_block_mode) { 41 | // In a block only exit after two blank lines 42 | if (lines.length < 2) return false; 43 | var second_last_line = lines[lines.length - 2].trimRight(); 44 | var block_ended = !!(!last_line && !second_last_line); 45 | if (!block_ended) return false; 46 | this.in_block_mode = false; 47 | return true; 48 | } 49 | 50 | if ( 51 | last_line && 52 | LINE_CONTINUATION_CHARS.indexOf( 53 | last_line.substr(last_line.length - 1) 54 | ) > -1 55 | ) { 56 | this.in_block_mode = true; 57 | return false; 58 | } 59 | try { 60 | compiler.parse(source, { filename: "", basedir: "__stdlib__" }); 61 | } catch (e) { 62 | if (e.is_eof && e.line === lines.length && e.col > 0) { 63 | return false; 64 | } 65 | this.in_block_mode = false; 66 | return true; 67 | } 68 | this.in_block_mode = false; 69 | return true; 70 | }, 71 | 72 | compile: (code, opts) => { 73 | opts = opts || {}; 74 | opts.keep_docstrings = true; 75 | opts.filename = ""; 76 | return streaming_compiler.compile(code, opts); 77 | }, 78 | 79 | runjs: (code) => { 80 | var ans = vm.runInContext(code, ctx); 81 | if (ans !== undefined || ans === null) { 82 | ctx.ρσ_repl_val = ans; 83 | var q = vm.runInContext("ρσ_repr(ρσ_repl_val)", ctx); 84 | ans = q === "undefined" ? ans.toString() : q; 85 | } 86 | return ans; 87 | }, 88 | 89 | init_completions: (completelib) => { 90 | find_completions = completelib(compiler); 91 | }, 92 | 93 | find_completions: (line) => { 94 | return find_completions(line, ctx); 95 | }, 96 | }; 97 | }; 98 | -------------------------------------------------------------------------------- /packages/jpython/try.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import subprocess 4 | import sys 5 | import os 6 | import shutil 7 | 8 | args = sys.argv[1:] 9 | source = None 10 | 11 | cmd = ['bin/jpython'] 12 | 13 | while args: 14 | if args[0] in ('-m', '-x'): 15 | cmd.append(args.pop(0)) 16 | elif args[0] == '-f': 17 | args.pop(0) 18 | source = args[0] 19 | cmd.append(source) 20 | else: 21 | break 22 | 23 | raw = ' '.join(args).replace('\\n', '\n') 24 | 25 | if os.path.exists('dev'): 26 | shutil.rmtree('dev') 27 | shutil.copytree('release', 'dev') 28 | subprocess.check_call(cmd[:1] + ['self']) 29 | if source: 30 | p = subprocess.Popen( 31 | ['node', '--stack-trace-limit=1000'] + cmd) 32 | else: 33 | p = subprocess.Popen( 34 | ['node', '--stack-trace-limit=1000'] + cmd, stdin=subprocess.PIPE) 35 | p.stdin.write(raw.encode('utf-8')) 36 | p.stdin.close() 37 | try: 38 | raise SystemExit(p.wait()) 39 | except KeyboardInterrupt: 40 | p.kill() 41 | raise SystemExit(1) 42 | -------------------------------------------------------------------------------- /packages/jpython/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es5", 5 | "lib": ["es6", "es2017", "dom"], 6 | "allowJs": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "isolatedModules": true, 12 | "jsx": "react-jsx", 13 | "noImplicitThis": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strictNullChecks": true, 19 | "rootDir": "./", 20 | "outDir": "dist" 21 | }, 22 | "exclude": ["node_modules", "dev", "dist", "release"], 23 | "watchOptions": { 24 | "watchFile": "useFsEvents", 25 | "watchDirectory": "useFsEvents", 26 | "fallbackPolling": "dynamicPriority", 27 | "synchronousWatchDirectory": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/mpc/Makefile: -------------------------------------------------------------------------------- 1 | # See http://www.multiprecision.org/mpc/download.html for versions: 2 | VERSION = 1.2.1 3 | 4 | BUILD = build 5 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 6 | DIST_NATIVE = ${CWD}/dist/native 7 | DIST_WASM = ${CWD}/dist/wasm 8 | DIST_JS = ${CWD}/dist/js 9 | 10 | all: ${DIST_NATIVE}/.built ${DIST_WASM}/.built 11 | 12 | ${BUILD}/mpc-${VERSION}.tar.gz: 13 | mkdir -p ${BUILD} 14 | cd ${BUILD} && curl https://ftp.gnu.org/gnu/mpc/mpc-${VERSION}.tar.gz -o mpc-${VERSION}.tar.gz 15 | 16 | .PHONY: source 17 | source: ${BUILD}/mpc-${VERSION}.tar.gz 18 | 19 | 20 | ## Native library 21 | 22 | ${BUILD}/native: ${BUILD}/mpc-${VERSION}.tar.gz 23 | rm -rf ${BUILD}/native 24 | cd ${BUILD} && mkdir native && tar xvf mpc-${VERSION}.tar.gz -C native --strip-components=1 25 | 26 | ${DIST_NATIVE}/.built: ${BUILD}/native 27 | cd ${BUILD}/native && CC="zig cc" AR="zig ar" CFLAGS="-O3" ./configure --prefix=${DIST_NATIVE} --with-gmp=${CWD}/../gmp/dist/native --with-mpfr=${CWD}/../mpfr/dist/native && make install 28 | touch ${DIST_NATIVE}/.built 29 | 30 | .PHONY: native 31 | native: ${DIST_NATIVE}/.built 32 | 33 | test-native: native 34 | cd ${BUILD}/native && make check 35 | 36 | 37 | ## WebAssembly library 38 | 39 | ${BUILD}/wasm: ${BUILD}/mpc-${VERSION}.tar.gz 40 | rm -rf ${BUILD}/wasm 41 | cd ${BUILD} && mkdir wasm && tar xvf mpc-${VERSION}.tar.gz -C wasm --strip-components=1 42 | 43 | ${DIST_WASM}/.built: ${BUILD}/wasm 44 | cd ${BUILD}/wasm && CC="zig cc -target wasm32-wasi -D_WASI_EMULATED_SIGNAL" AR="zig ar" RANLIB="zig ranlib" ABI=long CC_FOR_BUILD="zig cc" CFLAGS="-O3" \ 45 | ./configure --build i686-pc-linux-gnu --host=none --prefix=${DIST_WASM} --with-gmp=${CWD}/../gmp/dist/wasm --with-mpfr=${CWD}/../mpfr/dist/wasm 46 | cd ${BUILD}/wasm && make install 47 | touch ${DIST_WASM}/.built 48 | 49 | .PHONY: wasm 50 | wasm: ${DIST_WASM}/.built 51 | 52 | clean: 53 | rm -rf ${DIST_NATIVE} ${DIST_WASM} ${DIST_JS} ${CWD}/dist/tsconfig.tsbuildinfo ${BUILD}/native ${BUILD}/wasm node_modules -------------------------------------------------------------------------------- /packages/mpfr/Makefile: -------------------------------------------------------------------------------- 1 | # See https://www.mpfr.org/mpfr-current/#download 2 | VERSION = 4.1.0 3 | 4 | BUILD = build 5 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 6 | DIST_NATIVE = ${CWD}/dist/native 7 | DIST_WASM = ${CWD}/dist/wasm 8 | DIST_JS = ${CWD}/dist/js 9 | 10 | all: ${DIST_NATIVE}/.built ${DIST_WASM}/.built #${DIST_JS}/.built 11 | 12 | ${BUILD}/mpfr-${VERSION}.tar.xz: 13 | mkdir -p ${BUILD} 14 | cd ${BUILD} && curl https://www.mpfr.org/mpfr-current/mpfr-${VERSION}.tar.xz -o mpfr-${VERSION}.tar.xz 15 | 16 | .PHONY: source 17 | source: ${BUILD}/mpfr-${VERSION}.tar.xz 18 | 19 | 20 | ## Native library 21 | 22 | ${BUILD}/native: ${BUILD}/mpfr-${VERSION}.tar.xz 23 | rm -rf ${BUILD}/native 24 | cd ${BUILD} && mkdir native && tar xvf mpfr-${VERSION}.tar.xz -C native --strip-components=1 25 | 26 | ${DIST_NATIVE}/.built: ${BUILD}/native 27 | cd ${BUILD}/native && CC="zig cc" AR="zig ar" CFLAGS="-O3" ./configure --prefix=${DIST_NATIVE} --with-gmp=${CWD}/../gmp/dist/native && make install 28 | touch ${DIST_NATIVE}/.built 29 | 30 | .PHONY: native 31 | native: ${DIST_NATIVE}/.built 32 | 33 | test-native: native 34 | cd ${BUILD}/native && make check 35 | 36 | 37 | ## WebAssembly library 38 | 39 | ${BUILD}/wasm: ${BUILD}/mpfr-${VERSION}.tar.xz 40 | rm -rf ${BUILD}/wasm 41 | cd ${BUILD} && mkdir wasm && tar xvf mpfr-${VERSION}.tar.xz -C wasm --strip-components=1 42 | 43 | ${DIST_WASM}/.built: ${BUILD}/wasm 44 | cd ${BUILD}/wasm && CC="zig cc -target wasm32-wasi -D_WASI_EMULATED_SIGNAL" AR="zig ar" RANLIB="zig ranlib" ABI=standard CC_FOR_BUILD="zig cc" CFLAGS="-O3" \ 45 | ./configure --build i686-pc-linux-gnu --host=none --prefix=${DIST_WASM} --with-gmp=${CWD}/../gmp/dist/wasm 46 | cd ${BUILD}/wasm && make install 47 | touch ${DIST_WASM}/.built 48 | 49 | .PHONY: wasm 50 | wasm: ${DIST_WASM}/.built 51 | 52 | # Javascript interface library 53 | 54 | # node_modules: 55 | # npm ci 56 | 57 | # ${DIST_JS}/integer.js: src/integer.ts node_modules 58 | # npx tsc 59 | 60 | # test-integer: native 61 | # cd src && zig test integer.zig -lmpfr -lc -I${DIST_NATIVE}/include -L${DIST_NATIVE}/lib 62 | 63 | # ${DIST_JS}/integer.wasm: ${DIST_WASM}/.built 64 | # cd src/ && zig build-lib -target wasm32-wasi -dynamic -lc -I${DIST_WASM}/include -L${DIST_WASM}/lib ${DIST_WASM}/lib/libmpfr.a -O ReleaseSmall integer-export.zig 65 | # mkdir -p ${DIST_JS} 66 | # mv src/integer-export.wasm ${DIST_JS}/integer.wasm 67 | 68 | # ${DIST_JS}/.built: ${DIST_JS}/integer.wasm ${DIST_JS}/integer.js 69 | # touch ${DIST_JS}/.built 70 | 71 | # .PHONY: interface 72 | # interface: ${DIST_JS}/.built 73 | 74 | clean: 75 | rm -rf ${DIST_NATIVE} ${DIST_WASM} ${DIST_JS} ${CWD}/dist/tsconfig.tsbuildinfo ${BUILD}/native ${BUILD}/wasm node_modules -------------------------------------------------------------------------------- /packages/ntl/Makefile: -------------------------------------------------------------------------------- 1 | # See https://libntl.org/download.html 2 | VERSION = 11.5.1 3 | 4 | BUILD = build 5 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 6 | DIST_NATIVE = ${CWD}/dist/native 7 | DIST_WASM = ${CWD}/dist/wasm 8 | DIST_JS = ${CWD}/dist/js 9 | ZXX = ${CWD}/../../bin/z++ 10 | 11 | all: ${DIST_NATIVE}/.built ${DIST_WASM}/.built 12 | 13 | ${BUILD}/ntl-${VERSION}.tar.gz: 14 | mkdir -p ${BUILD} 15 | cd ${BUILD} && curl https://libntl.org/ntl-${VERSION}.tar.gz -o ntl-${VERSION}.tar.gz 16 | 17 | .PHONY: source 18 | source: ${BUILD}/ntl-${VERSION}.tar.gz 19 | 20 | 21 | ## Native library 22 | 23 | ${BUILD}/native: ${BUILD}/ntl-${VERSION}.tar.gz 24 | rm -rf ${BUILD}/native 25 | cd ${BUILD} && mkdir native && tar xvf ntl-${VERSION}.tar.gz -C native --strip-components=1 26 | 27 | #${DIST_NATIVE}/.built: export DYLD_LIBRARY_PATH = ${CWD}/../gmp/dist/native/lib 28 | 29 | ${DIST_NATIVE}/.built: ${BUILD}/native 30 | cd ${BUILD}/native/src && ./configure \ 31 | CXX="zig c++" RANLIB="zig ranlib" AR="zig ar" \ 32 | GMP_PREFIX=${CWD}/../gmp/dist/native \ 33 | GF2X_PREFIX=${CWD}/../gf2x/dist/native \ 34 | NTL_GMP_LIP=off \ 35 | PREFIX=${DIST_NATIVE} 36 | cd ${BUILD}/native/src && make && make install 37 | touch ${DIST_NATIVE}/.built 38 | 39 | .PHONY: native 40 | native: ${DIST_NATIVE}/.built 41 | 42 | test-native: native 43 | cd ${BUILD}/native && make check 44 | 45 | 46 | ## WebAssembly library 47 | 48 | ${BUILD}/wasm: ${BUILD}/ntl-${VERSION}.tar.gz 49 | rm -rf ${BUILD}/wasm 50 | cd ${BUILD} && mkdir wasm && tar xvf ntl-${VERSION}.tar.gz -C wasm --strip-components=1 51 | 52 | ${DIST_WASM}/.built: ${BUILD}/wasm 53 | cd ${BUILD}/wasm/src && ./configure \ 54 | CXX=${ZXX} RANLIB="zig ranlib" AR="zig ar" \ 55 | NATIVE=off TUNE=generic \ 56 | NTL_THREADS=off NTL_EXCEPTIONS=off NTL_GMP_LIP=off\ 57 | PREFIX=${DIST_WASM} \ 58 | GMP_PREFIX=${CWD}/../gmp/dist/wasm GF2X_PREFIX=${CWD}/../gf2x/dist/wasm \ 59 | && make && make install 60 | touch ${DIST_WASM}/.built 61 | 62 | .PHONY: wasm 63 | wasm: ${DIST_WASM}/.built 64 | 65 | clean: 66 | rm -rf ${DIST_NATIVE} ${DIST_WASM} ${DIST_JS} ${CWD}/dist/tsconfig.tsbuildinfo ${BUILD}/native ${BUILD}/wasm node_modules -------------------------------------------------------------------------------- /packages/ntl/TODO.md: -------------------------------------------------------------------------------- 1 | Note that we are currently building the WASM version with: 2 | 3 | ``` 4 | NTL_GMP_LIP=off 5 | ``` 6 | 7 | so presumably it's really slow for big numbers. 8 | 9 | We're doing this for native because: 10 | - on native macos it accidentally gets the systemwide libgmp which breaks 11 | - on wasm it complains about limb size inconsistency 12 | -------------------------------------------------------------------------------- /packages/openssl/Makefile: -------------------------------------------------------------------------------- 1 | CWD:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | BUILD = ${CWD}/build 3 | DIST_NATIVE = ${CWD}/dist/native 4 | DIST_WASM = ${CWD}/dist/wasm 5 | 6 | .PHONEY: all 7 | all: ${DIST_NATIVE}/.built 8 | 9 | 10 | ${BUILD}/openssl: 11 | mkdir -p ${BUILD} 12 | cd ${BUILD} && git clone --depth 1 --branch openssl-3.0.0 https://github.com/openssl/openssl.git openssl 13 | 14 | ## Native library 15 | 16 | ${BUILD}/native: ${BUILD}/openssl 17 | rm -rf ${BUILD}/native 18 | cd ${BUILD} && git clone openssl native 19 | 20 | ${DIST_NATIVE}/.built: ${BUILD}/native 21 | cd ${BUILD}/native && CC="zig cc" AR="zig ar" ./Configure --prefix=${DIST_NATIVE} 22 | cd ${BUILD}/native && make -j8 23 | cd ${BUILD}/native && make install 24 | touch ${DIST_NATIVE}/.built 25 | 26 | .PHONY: native 27 | native: ${DIST_NATIVE}/.built 28 | 29 | clean: 30 | rm -rf build dist 31 | -------------------------------------------------------------------------------- /packages/openssl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sagemath/python", 3 | "version": "1.0.3", 4 | "description": "Python is a programming language that lets you work quickly and integrate systems more effectively.", 5 | "files": ["dist/*", "README.md", "src/*", "*.sh", "package.json"], 6 | "scripts": { 7 | "build": "make all", 8 | "clean": "make clean" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/sagemathinc/sagejs.git" 13 | }, 14 | "keywords": ["python", "wasm", "emscripten"], 15 | "author": "William Stein", 16 | "license": "PSF", 17 | "bugs": { 18 | "url": "https://github.com/sagemathinc/sagejs/issues" 19 | }, 20 | "homepage": "https://github.com/sagemathinc/sagejs/tree/main/src/packages/python" 21 | } 22 | -------------------------------------------------------------------------------- /packages/pari/Makefile: -------------------------------------------------------------------------------- 1 | # https://pari.math.u-bordeaux.fr/download.html 2 | VERSION = 2.13.3 3 | 4 | CWD:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 5 | BUILD = ${CWD}/build 6 | DIST_NATIVE = ${CWD}/dist/native 7 | DIST_WASM = ${CWD}/dist/wasm 8 | DIST_JS = ${CWD}/dist/js 9 | 10 | ZCC = ${CWD}/../../bin/zcc 11 | GMP = ${CWD}/../gmp/dist 12 | WASMPOSIX = ${CWD}/../wasm-posix/dist 13 | 14 | 15 | all: native wasm 16 | 17 | 18 | ############# 19 | # Source code 20 | ############# 21 | ${BUILD}/pari-${VERSION}.tar.gz: 22 | mkdir -p ${BUILD} 23 | # retry-max-time: For some reason pari.math.u-bordeaux.fr is VERY flaky. 24 | cd ${BUILD} && curl --retry-max-time 600 https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-${VERSION}.tar.gz -o pari-${VERSION}.tar.gz 25 | 26 | .PHONY: source 27 | source: ${BUILD}/pari-${VERSION}.tar.gz 28 | 29 | 30 | # NATIVE 31 | 32 | ${BUILD}/native: ${BUILD}/pari-${VERSION}.tar.gz 33 | rm -rf ${BUILD}/native 34 | cd ${BUILD} && mkdir native && tar xf pari-${VERSION}.tar.gz -C native --strip-components=1 35 | 36 | ${DIST_NATIVE}/.built: ${BUILD}/native 37 | cd ${BUILD}/native && CC="zig cc" AR="zig ar" RANLIB="zig ranlib" \ 38 | ./Configure --static --prefix=${DIST_NATIVE} --graphic=none --with-gmp=${GMP}/native 39 | cd ${BUILD}/native/O* && make AR="zig ar" RANLIB="zig ranlib" -j8 gp 40 | cd ${BUILD}/native/O* && make install 41 | touch ${DIST_NATIVE}/.built 42 | 43 | .PHONY: native 44 | native: ${DIST_NATIVE}/.built 45 | 46 | .PHONY: run-python-native 47 | run-native: ${DIST_NATIVE}/.built 48 | ${DIST_NATIVE}/bin/gp 49 | 50 | # WASM 51 | 52 | ${BUILD}/wasm: ${BUILD}/pari-${VERSION}.tar.gz 53 | rm -rf ${BUILD}/wasm 54 | cd ${BUILD} && mkdir wasm && tar xf pari-${VERSION}.tar.gz -C wasm --strip-components=1 55 | 56 | ${DIST_WASM}/.built: ${BUILD}/wasm 57 | cd ${BUILD}/wasm && CC=${ZCC} AR="zig ar" RANLIB="zig ranlib" \ 58 | ./Configure --static --host=wasm-wasi --prefix=${DIST_WASM} --graphic=none --with-gmp=${GMP}/wasm 59 | # Missing bits/ headers with zig: 60 | cd ${BUILD}/wasm/O* && mkdir -p bits && cd bits && echo "#define __jmp_buf int"> setjmp.h && echo "" > wordsize.h 61 | cd ${BUILD}/wasm/O* && make -j8 AR="zig ar" RANLIB="zig ranlib" gp 62 | cd ${BUILD}/wasm/O* && make AR="zig ar" RANLIB="zig ranlib" install 63 | # Also rebuild a new version of gp-sta.wasm with the extra posix lib included by abusing EXPORT_EXE. 64 | cd ${BUILD}/wasm/O* && rm gp-sta && make EXPORT_EXE=${WASMPOSIX}/libwasmposix.a gp-sta 65 | cp ${BUILD}/wasm/O*/gp-sta.wasm ${DIST_WASM}/bin 66 | mkdir -p ${DIST_WASM}/include/bits && cp ${BUILD}/wasm/O*/bits/* ${DIST_WASM}/include/bits/ 67 | # Also build a new version of gp-sta.wasm with the extra posix lib included. 68 | touch ${DIST_WASM}/.built 69 | 70 | .PHONY: wasm 71 | wasm: ${DIST_WASM}/.built 72 | 73 | .PHONY: run-wasm 74 | run-wasm: ${DIST_WASM}/.built 75 | ${DIST_WASM}/bin/gp 76 | 77 | 78 | clean: 79 | rm -rf ${BUILD} ${DIST_WASM} ${DIST_NATIVE} ${PREFIX_NATIVE} ${PREFIX_WASM} 80 | 81 | -------------------------------------------------------------------------------- /packages/pari/TODO.md: -------------------------------------------------------------------------------- 1 | WASM Pari is definitely being built without GMP. This 2 | is making it MUCH MUCH slower. 3 | 4 | Here is how to tell if GMP is being used or not: 5 | ```sh 6 | ~/jsage/packages/pari$ make run-wasm 3 | Date: Sun, 5 Jul 2020 17:37:43 +0200 4 | Subject: [PATCH] disable-set_inheritable 5 | 6 | --- 7 | Python/fileutils.c | 3 +++ 8 | 1 file changed, 3 insertions(+) 9 | 10 | diff --git a/Python/fileutils.c b/Python/fileutils.c 11 | index 769ab59..a784789 100644 12 | --- a/Python/fileutils.c 13 | +++ b/Python/fileutils.c 14 | @@ -1134,6 +1134,9 @@ _Py_get_inheritable(int fd) 15 | static int 16 | set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works) 17 | { 18 | +#ifdef EMSCRIPTEN 19 | + return 0; 20 | +#endif 21 | #ifdef MS_WINDOWS 22 | HANDLE handle; 23 | DWORD flags; 24 | -- 25 | 2.17.1 26 | 27 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/0002-dont-test-undecodable-filenames.patch: -------------------------------------------------------------------------------- 1 | From ba3bbdfc19c8d1d778c36af0424c56e193460170 Mon Sep 17 00:00:00 2001 2 | From: Michael Droettboom 3 | Date: Sun, 5 Jul 2020 17:38:21 +0200 4 | Subject: [PATCH] dont-test-undecodable-filenames 5 | 6 | --- 7 | Lib/test/support/__init__.py | 2 ++ 8 | 1 file changed, 2 insertions(+) 9 | 10 | diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py 11 | index aee3737..d0be99f 100644 12 | --- a/Lib/test/support/__init__.py 13 | +++ b/Lib/test/support/__init__.py 14 | @@ -809,6 +809,8 @@ if os.name == 'nt': 15 | 'Unicode filename tests may not be effective' 16 | % (TESTFN_UNENCODABLE, TESTFN_ENCODING)) 17 | TESTFN_UNENCODABLE = None 18 | +elif sys.platform == 'emscripten': 19 | + pass 20 | # Mac OS X denies unencodable filenames (invalid utf-8) 21 | elif sys.platform != 'darwin': 22 | try: 23 | -- 24 | 2.17.1 25 | 26 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/0005-add-emscripten-host.patch: -------------------------------------------------------------------------------- 1 | --- 2 | config.sub | 9 ++++++++- 3 | configure | 6 ++++++ 4 | configure.ac | 5 +++++ 5 | 3 files changed, 19 insertions(+), 1 deletion(-) 6 | 7 | diff --git a/config.sub b/config.sub 8 | index ba37cf9..0d22b33 100755 9 | --- a/config.sub 10 | +++ b/config.sub 11 | @@ -118,7 +118,8 @@ case $maybe_os in 12 | linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ 13 | knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ 14 | kopensolaris*-gnu* | cloudabi*-eabi* | \ 15 | - storm-chaos* | os2-emx* | rtmk-nova*) 16 | + storm-chaos* | os2-emx* | rtmk-nova* | \ 17 | + emscripten) 18 | os=-$maybe_os 19 | basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` 20 | ;; 21 | @@ -377,6 +378,7 @@ case $basic_machine in 22 | | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ 23 | | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ 24 | | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ 25 | + | wasm32 \ 26 | | avr-* | avr32-* \ 27 | | ba-* \ 28 | | be32-* | be64-* \ 29 | @@ -527,6 +529,9 @@ case $basic_machine in 30 | asmjs) 31 | basic_machine=asmjs-unknown 32 | ;; 33 | + wasm32) 34 | + basic_machine=wasm32-unknown 35 | + ;; 36 | aux) 37 | basic_machine=m68k-apple 38 | os=-aux 39 | @@ -1522,6 +1527,8 @@ case $os in 40 | ;; 41 | esac 42 | ;; 43 | + -emscripten) 44 | + ;; 45 | -nacl*) 46 | ;; 47 | -ios) 48 | diff --git a/configure b/configure 49 | index 8dcdbf1..e1195d2 100755 50 | --- a/configure 51 | +++ b/configure 52 | @@ -3307,6 +3307,9 @@ then 53 | *-*-vxworks*) 54 | ac_sys_system=VxWorks 55 | ;; 56 | + wasm32-*-*) 57 | + ac_sys_system=Emscripten 58 | + ;; 59 | *) 60 | # for now, limit cross builds to known configurations 61 | MACHDEP="unknown" 62 | @@ -3357,6 +3360,9 @@ if test "$cross_compiling" = yes; then 63 | *-*-vxworks*) 64 | _host_cpu=$host_cpu 65 | ;; 66 | + wasm32-*-*) 67 | + _host_cpu= 68 | + ;; 69 | *) 70 | # for now, limit cross builds to known configurations 71 | MACHDEP="unknown" 72 | diff --git a/configure.ac b/configure.ac 73 | index b1e4c6c..dbfae6d 100644 74 | --- a/configure.ac 75 | +++ b/configure.ac 76 | @@ -403,6 +403,9 @@ then 77 | *-*-vxworks*) 78 | ac_sys_system=VxWorks 79 | ;; 80 | + wasm32-*-*) 81 | + ac_sys_system=Emscripten 82 | + ;; 83 | *) 84 | # for now, limit cross builds to known configurations 85 | MACHDEP="unknown" 86 | @@ -451,6 +454,8 @@ if test "$cross_compiling" = yes; then 87 | ;; 88 | *-*-vxworks*) 89 | _host_cpu=$host_cpu 90 | + wasm32-*-*) 91 | + _host_cpu= 92 | ;; 93 | *) 94 | # for now, limit cross builds to known configurations 95 | -- 96 | 2.17.1 97 | 98 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/0006-fix-Py_Sigset_Converter.patch: -------------------------------------------------------------------------------- 1 | From 63fd6ee84261ff357d3d3b56a35b756c4d13ce69 Mon Sep 17 00:00:00 2001 2 | From: Roman Yurchak 3 | Date: Sun, 5 Jul 2020 21:17:10 +0200 4 | Subject: [PATCH] fix Py_Sigset_Converter 5 | 6 | --- 7 | Modules/posixmodule.c | 7 +++++++ 8 | Modules/posixmodule.h | 2 -- 9 | 2 files changed, 7 insertions(+), 2 deletions(-) 10 | 11 | diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c 12 | index c984e2e..9d1bbee 100644 13 | --- a/Modules/posixmodule.c 14 | +++ b/Modules/posixmodule.c 15 | @@ -1486,6 +1486,13 @@ error: 16 | Py_DECREF(iterator); 17 | return 0; 18 | } 19 | +#else 20 | +int 21 | +_Py_Sigset_Converter(PyObject *obj, void *addr) 22 | +{ 23 | + PyErr_SetFromErrno(PyExc_OSError); 24 | + return 0; 25 | +} 26 | #endif /* HAVE_SIGSET_T */ 27 | 28 | #ifdef MS_WINDOWS 29 | diff --git a/Modules/posixmodule.h b/Modules/posixmodule.h 30 | index 711ac68..5452ffb 100644 31 | --- a/Modules/posixmodule.h 32 | +++ b/Modules/posixmodule.h 33 | @@ -23,9 +23,7 @@ PyAPI_FUNC(int) _Py_Gid_Converter(PyObject *, gid_t *); 34 | # define HAVE_SIGSET_T 35 | #endif 36 | 37 | -#ifdef HAVE_SIGSET_T 38 | PyAPI_FUNC(int) _Py_Sigset_Converter(PyObject *, void *); 39 | -#endif /* HAVE_SIGSET_T */ 40 | #endif /* Py_LIMITED_API */ 41 | 42 | #ifdef __cplusplus 43 | -- 44 | 2.17.1 45 | 46 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/0008-setup.patch: -------------------------------------------------------------------------------- 1 | --- a/setup.py 2021-08-26 06:00:42.473541841 +0000 2 | +++ b/setup.py 2021-08-26 06:00:21.904005444 +0000 3 | @@ -117,6 +117,11 @@ 4 | 1) 'dir' is not already in 'dirlist' 5 | 2) 'dir' actually exists, and is a directory. 6 | """ 7 | + if dir.endswith('linux-gnu'): 8 | + if dir.startswith('/usr/lib'): 9 | + dir = '/usr/lib' 10 | + elif dir.startswith('/usr/include'): 11 | + dir = '/usr/include' 12 | if dir is None or not os.path.isdir(dir) or dir in dirlist: 13 | return 14 | for i, path in enumerate(dirlist): 15 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/README.md: -------------------------------------------------------------------------------- 1 | These are initially from pyodide. 2 | 3 | 0008-setup.patch - I need this to get the build to work with zig. 4 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/ctypes-dont-deref-function-pointer.patch: -------------------------------------------------------------------------------- 1 | From 3f11694d7587e782530118d176306eeb6eebf5d1 Mon Sep 17 00:00:00 2001 2 | From: Hood 3 | Date: Wed, 23 Jun 2021 13:47:30 -0700 4 | Subject: [PATCH] Don't dereference function pointer 5 | 6 | Ctypes thinks that the result of dlsym is a pointer to the function pointer, so 7 | it should call it like `result = (*f)(args)`. Probably this is true for the 8 | native dlsym, but our dlsym returns an index into the indirect call table 9 | "wasmTable", in particular it isn't even aligned like a pointer should be. 10 | This patch fixes it so that it calls it like `result = f(args)` instead. 11 | 12 | --- 13 | Modules/_ctypes/_ctypes.c | 6 +++++- 14 | 1 file changed, 5 insertions(+), 1 deletion(-) 15 | 16 | diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c 17 | index ceae67e..44f2d76 100644 18 | --- a/Modules/_ctypes/_ctypes.c 19 | +++ b/Modules/_ctypes/_ctypes.c 20 | @@ -771,7 +771,11 @@ CDataType_in_dll(PyObject *type, PyObject *args) 21 | return NULL; 22 | } 23 | #endif 24 | - return PyCData_AtAddress(type, address); 25 | + CDataObject *ob = (CDataObject *)GenericPyCData_new(type, NULL, NULL); 26 | + if (ob == NULL) 27 | + return NULL; 28 | + *(void **)ob->b_ptr = address; 29 | + return (PyObject*)ob; 30 | } 31 | 32 | static const char from_param_doc[] = 33 | -- 34 | 2.17.1 35 | 36 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/remove-duplicate-symbols-from-cfield.c.patch: -------------------------------------------------------------------------------- 1 | From fc3b69f9afa779185a60834cf1817c22706edcd1 Mon Sep 17 00:00:00 2001 2 | From: Hood 3 | Date: Tue, 22 Jun 2021 20:12:45 -0700 4 | Subject: [PATCH] Remove duplicate symbols from cfield.c 5 | 6 | These symbols are already defined by libffi. 7 | --- 8 | Modules/_ctypes/cfield.c | 26 -------------------------- 9 | 1 file changed, 26 deletions(-) 10 | 11 | diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c 12 | index 7ebd4ba..7a63ab7 100644 13 | --- a/Modules/_ctypes/cfield.c 14 | +++ b/Modules/_ctypes/cfield.c 15 | @@ -1635,31 +1635,5 @@ typedef struct _ffi_type 16 | } ffi_type; 17 | */ 18 | 19 | -/* align and size are bogus for void, but they must not be zero */ 20 | -ffi_type ffi_type_void = { 1, 1, FFI_TYPE_VOID }; 21 | - 22 | -ffi_type ffi_type_uint8 = { 1, 1, FFI_TYPE_UINT8 }; 23 | -ffi_type ffi_type_sint8 = { 1, 1, FFI_TYPE_SINT8 }; 24 | - 25 | -ffi_type ffi_type_uint16 = { 2, 2, FFI_TYPE_UINT16 }; 26 | -ffi_type ffi_type_sint16 = { 2, 2, FFI_TYPE_SINT16 }; 27 | - 28 | -ffi_type ffi_type_uint32 = { 4, INT_ALIGN, FFI_TYPE_UINT32 }; 29 | -ffi_type ffi_type_sint32 = { 4, INT_ALIGN, FFI_TYPE_SINT32 }; 30 | - 31 | -ffi_type ffi_type_uint64 = { 8, LONG_LONG_ALIGN, FFI_TYPE_UINT64 }; 32 | -ffi_type ffi_type_sint64 = { 8, LONG_LONG_ALIGN, FFI_TYPE_SINT64 }; 33 | - 34 | -ffi_type ffi_type_float = { sizeof(float), FLOAT_ALIGN, FFI_TYPE_FLOAT }; 35 | -ffi_type ffi_type_double = { sizeof(double), DOUBLE_ALIGN, FFI_TYPE_DOUBLE }; 36 | - 37 | -#ifdef ffi_type_longdouble 38 | -#undef ffi_type_longdouble 39 | -#endif 40 | - /* This is already defined on OSX */ 41 | -ffi_type ffi_type_longdouble = { sizeof(long double), LONGDOUBLE_ALIGN, 42 | - FFI_TYPE_LONGDOUBLE }; 43 | - 44 | -ffi_type ffi_type_pointer = { sizeof(void *), VOID_P_ALIGN, FFI_TYPE_POINTER }; 45 | 46 | /*---------------- EOF ----------------*/ 47 | -- 48 | 2.17.1 49 | 50 | -------------------------------------------------------------------------------- /packages/python/src/python-patches/xfail-core-ctypes-tests-that-don-t-work.patch: -------------------------------------------------------------------------------- 1 | From a4aec920c76ebcb8360350ff0046c7a0c74c0cf2 Mon Sep 17 00:00:00 2001 2 | From: Hood 3 | Date: Thu, 24 Jun 2021 14:55:10 -0700 4 | Subject: [PATCH] xfail core ctypes tests that don't work 5 | 6 | PyCode_SetExtra doesn't work and ctypes doesn't seem to work with variadic functions including PyUnicode_FromFormat/ 7 | --- 8 | Lib/test/test_code.py | 7 +++++-- 9 | Lib/test/test_unicode.py | 1 + 10 | 2 files changed, 6 insertions(+), 2 deletions(-) 11 | 12 | diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py 13 | index ac3dde7..d91a350 100644 14 | --- a/Lib/test/test_code.py 15 | +++ b/Lib/test/test_code.py 16 | @@ -389,6 +389,7 @@ if check_impl_detail(cpython=True) and ctypes is not None: 17 | ctypes.c_voidp(100)), 0) 18 | 19 | def test_free_called(self): 20 | + raise unittest.SkipTest("PyCode_SetExtra is broken") 21 | # Verify that the provided free function gets invoked 22 | # when the code object is cleaned up. 23 | f = self.get_func() 24 | @@ -398,6 +399,7 @@ if check_impl_detail(cpython=True) and ctypes is not None: 25 | self.assertEqual(LAST_FREED, 100) 26 | 27 | def test_get_set(self): 28 | + raise unittest.SkipTest("PyCode_SetExtra is broken") 29 | # Test basic get/set round tripping. 30 | f = self.get_func() 31 | 32 | @@ -414,6 +416,7 @@ if check_impl_detail(cpython=True) and ctypes is not None: 33 | del f 34 | 35 | def test_free_different_thread(self): 36 | + raise unittest.SkipTest("Requires threading") 37 | # Freeing a code object on a different thread then 38 | # where the co_extra was set should be safe. 39 | f = self.get_func() 40 | @@ -438,8 +441,8 @@ def test_main(verbose=None): 41 | from test import test_code 42 | run_doctest(test_code, verbose) 43 | tests = [CodeTest, CodeConstsTest, CodeWeakRefTest] 44 | - if check_impl_detail(cpython=True) and ctypes is not None: 45 | - tests.append(CoExtra) 46 | + # if check_impl_detail(cpython=True) and ctypes is not None: 47 | + # tests.append(CoExtra) 48 | run_unittest(*tests) 49 | 50 | if __name__ == "__main__": 51 | -------------------------------------------------------------------------------- /packages/python/src/xgcd.py: -------------------------------------------------------------------------------- 1 | # A standard xgcd implementation for Python copied from a random webpage. 2 | # This of course quickly overflows with Javascript "integers" = doubles' 3 | def xgcd(a, b): 4 | prevx, x = 1, 0 5 | prevy, y = 0, 1 6 | while b: 7 | q, r = divmod(a, b) 8 | x, prevx = prevx - q * x, x 9 | y, prevy = prevy - q * y, y 10 | a, b = b, r 11 | return a, prevx, prevy 12 | 13 | 14 | def bench_xgcd(n=10**6): 15 | from time import time 16 | t = time() 17 | s = 0 18 | for i in range(n): 19 | s += xgcd(92250 - i, 922350 + i)[0] 20 | print(s, time() - t) 21 | 22 | 23 | def inverse_mod(a, N): 24 | """ 25 | Compute multiplicative inverse of a modulo N. 26 | """ 27 | if a == 1 or N <= 1: # common special cases 28 | return a % N 29 | [g, s, _] = xgcd(a, N) 30 | if g != 1: 31 | raise ZeroDivisionError 32 | b = s % N 33 | if b < 0: 34 | b += N 35 | return b 36 | 37 | 38 | if __name__ == "__main__": 39 | print("inverse_mod(7,15) = ", inverse_mod(7,15)) 40 | bench_xgcd() -------------------------------------------------------------------------------- /packages/readline/Makefile: -------------------------------------------------------------------------------- 1 | # See https://ftp.gnu.org/gnu/readline/?C=M;O=D 2 | VERSION = 8.1 3 | 4 | # https://stackoverflow.com/questions/18136918/how-to-get-current-relative-directory-of-your-makefile 5 | INIT_CWD:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 6 | DIST = ${INIT_CWD}/dist 7 | BUILD = ${INIT_CWD}/build 8 | DIST_NATIVE = ${DIST}/native 9 | DIST_WASM = ${DIST}/wasm 10 | 11 | all: native wasm 12 | 13 | ${BUILD}/readline-${VERSION}.tar.gz: 14 | mkdir -p ${BUILD} 15 | cd ${BUILD} && curl https://ftp.gnu.org/gnu/readline/readline-${VERSION}.tar.gz -o readline-${VERSION}.tar.gz 16 | 17 | # NATIVE 18 | 19 | ${BUILD}/native: ${BUILD}/readline-${VERSION}.tar.gz 20 | rm -rf ${DIST_NATIVE} && mkdir -p ${DIST_NATIVE} 21 | cd ${BUILD} && mkdir native && tar xf readline-${VERSION}.tar.gz -C native --strip-components=1 22 | 23 | ${DIST_NATIVE}/.built: ${BUILD}/native 24 | cd ${BUILD}/native && AR="zig ar" CC="zig cc" ./configure --prefix=${DIST_NATIVE} 25 | cd ${BUILD}/native && make && make install && touch ${DIST_NATIVE}/.built 26 | 27 | .PHONY: native 28 | native: ${DIST_NATIVE}/.built 29 | 30 | 31 | # WASM 32 | 33 | ${BUILD}/wasm: ${BUILD}/readline-${VERSION}.tar.gz 34 | rm -rf ${DIST_WASM} && mkdir -p ${DIST_WASM} 35 | cd ${BUILD} && mkdir wasm && tar xf readline-${VERSION}.tar.gz -C wasm --strip-components=1 36 | 37 | ${DIST_WASM}/.built: ${BUILD}/wasm 38 | # Horrible temporary hack because of missing bits/ headers with zig. Will have to address this somehow... 39 | cd ${BUILD}/wasm && mkdir -p bits && cp /usr/include/bits/setjmp.h /usr/include/bits/wordsize.h bits/ 40 | cd ${BUILD}/wasm && AR="zig ar" CC="zig cc -target wasm32-wasi -D_WASI_EMULATED_SIGNAL -DSIG_BLOCK -Dset -Doset" ./configure --build i686-pc-linux-gnu --host=none --prefix=${DIST_WASM} 41 | cd ${BUILD}/wasm && make && make install 42 | echo "build worked!" 43 | touch ${DIST_WASM}/.built 44 | 45 | .PHONY: wasm 46 | wasm: ${DIST_WASM}/.built 47 | 48 | .PHONY: clean 49 | clean: 50 | rm -rf ${BUILD} ${DIST} 51 | -------------------------------------------------------------------------------- /packages/readline/NOTES.md: -------------------------------------------------------------------------------- 1 | I obviously didn't yet figure out how to build readline for WASM. I don't even know 2 | if it is possible/impossible/easy/hard... 3 | 4 | One thing though -- the native build has an example in 5 | ```sh 6 | build/native/examples/rlfe 7 | ``` 8 | 9 | and if you build that then do 10 | 11 | ```sh 12 | ~/jsage/packages/readline/build/native/examples/rlfe$ ./rlfe wasmer run /home/user/jsage/packages/pari/dist/wasm/bin/gp-sta.wasm --mapdir /:/ 13 | ``` 14 | 15 | then you get a much more usable version of the wasm pari. Obviously there is 16 | no tab completion, but at least you can edit the line you're about to input. 17 | -------------------------------------------------------------------------------- /packages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es5", 5 | "lib": ["es5", "es6", "es2017", "dom"], 6 | "allowJs": true, 7 | "declaration": true, 8 | "downlevelIteration": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "isolatedModules": true, 12 | "noImplicitThis": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "resolveJsonModule": true, 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strictNullChecks": true 19 | }, 20 | "watchOptions": { 21 | "watchFile": "useFsEvents", 22 | "watchDirectory": "useFsEvents", 23 | "fallbackPolling": "dynamicPriority", 24 | "synchronousWatchDirectory": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/wasi/Makefile: -------------------------------------------------------------------------------- 1 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | DIST = ${CWD}/dist 3 | 4 | all: ${DIST}/.built 5 | 6 | ${DIST}/.built: 7 | npm ci 8 | npm run build 9 | touch ${DIST}/.built 10 | 11 | clean: 12 | rm -rf ${DIST} node_modules tsconfig.tsbuildinfo 13 | -------------------------------------------------------------------------------- /packages/wasi/README.md: -------------------------------------------------------------------------------- 1 | # `@jsage/wasi` 2 | 3 | Javascript library for interacting with WASI Modules in Node.js. 4 | 5 | (TODO: and in the Browser.) 6 | 7 | This is a fork of version 0.12.0 of @wasmer/wasi to keep it alive, since the Wasmer company decided to end it, and I would like to use it in [JSage](https://github.com/sagemathinc/JSage). 8 | 9 | ## Table of Contents 10 | 11 | - [Features](#features) 12 | - [Installation](#installation) 13 | - [Quick Start](#quick-start) 14 | 15 | ## Features 16 | 17 | `@jsage/wasi` uses the same API as the [future WASI integration in Node](https://github.com/nodejs/wasi). 18 | 19 | However, `@jsage/wasi` is focused on: 20 | 21 | - Bringing [WASI](https://github.com/webassembly/wasi) to an Isomorphic context (Node.js and the Browser) 22 | - Make it easy to plug in different filesystems (via [wasmfs](https://github.com/wasmerio/wasmer-js/tree/master/packages/wasmfs)) 23 | - Make it type-safe using [Typescript](http://www.typescriptlang.org/) 24 | - Pure JavaScript implementation (no Native bindings needed) 25 | - Very small 26 | 27 | ## Installation 28 | 29 | To install `@jsage/wasi`, run this command: 30 | 31 | ```bash 32 | npm install @jsage/wasi 33 | ``` 34 | 35 | ## Quick Start 36 | 37 | **This quick start is for node.** It's something like this. See lib/src/wasm.ts in the JSage source code for something that uses @jsage/wasi in a real application for a better tested example. 38 | 39 | ```js 40 | import { WASI } from "@jsage/wasi"; 41 | import fs from "fs"; 42 | import nodeBindings from "@jsage/wasi/dist/bindings/node"; 43 | 44 | const wasi = new WASI({ 45 | args: [], 46 | env: {}, 47 | bindings: {...nodeBindings, fs} 48 | }); 49 | 50 | const source = await readFile(pathToWasm); 51 | const typedArray = new Uint8Array(source); 52 | const result = await WebAssembly.instantiate(typedArray, wasmOpts); 53 | wasi.start(result.instance); 54 | ``` 55 | -------------------------------------------------------------------------------- /packages/wasi/bin/run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const { run } = require(".."); 3 | if (process.argv.length <= 2) { 4 | throw Error("must provide path to .wasm code"); 5 | } 6 | process.argv = process.argv.slice(2); 7 | let [name] = process.argv; 8 | run(name); 9 | -------------------------------------------------------------------------------- /packages/wasi/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | ".(ts|tsx)": "ts-jest" 4 | }, 5 | testEnvironment: "node", 6 | testRegex: "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx)$", 7 | moduleFileExtensions: ["ts", "tsx", "js"], 8 | coveragePathIgnorePatterns: ["/node_modules/", "/test/"], 9 | coverageThreshold: { 10 | global: { 11 | branches: 90, 12 | functions: 95, 13 | lines: 95, 14 | statements: 95 15 | } 16 | }, 17 | collectCoverageFrom: ["src/*.{ts}"], 18 | globals: { 19 | window: true, 20 | global: true, 21 | self: true 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /packages/wasi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jsage/wasi", 3 | "version": "0.14.0", 4 | "description": "Isomorphic Javascript library for interacting with WASI Modules in Node.js and the Browser.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "clean": "rm -rf dist", 8 | "build": "tsc -b", 9 | "tsc": "tsc -w", 10 | "dev": "watch \"npm run build:dev\" src", 11 | "test-precompile": "cd test/rs && make all", 12 | "test": "jest --config jest.config.js --verbose", 13 | "test:watch": "jest --config jest.config.js --watch --verbose", 14 | "docs": "typedoc src/ --out docs --target es6 --theme minimal --mode file" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/sagemathinc/jsage.git" 19 | }, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "keywords": [ 24 | "wasi", 25 | "webassembly", 26 | "wasm", 27 | "wasmer", 28 | "abi", 29 | "esm", 30 | "es", 31 | "module" 32 | ], 33 | "bin": { 34 | "wasi-run": "./bin/run.js" 35 | }, 36 | "author": "William Stein ", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/sagemathinc/jsage/issues" 40 | }, 41 | "homepage": "https://github.com/sagemathinc/JSage/tree/main/packages/wasi", 42 | "dependencies": { 43 | "browser-hrtime": "^1.1.8", 44 | "path-browserify": "^1.0.0", 45 | "randomfill": "^1.0.4" 46 | }, 47 | "devDependencies": { 48 | "@types/node": "^16.11.12", 49 | "@types/path-browserify": "^1.0.0", 50 | "typescript": "^4.5.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/wasi/src/bindings/browser.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import { randomFillSync } from "randomfill"; 3 | import path from "path-browserify"; 4 | import hrtime from "browser-hrtime"; 5 | 6 | import { WASIBindings, WASIExitError, WASIKillError } from "../index"; 7 | 8 | const bindings: WASIBindings = { 9 | hrtime: hrtime.bigint, 10 | exit: (code: number | null) => { 11 | throw new WASIExitError(code); 12 | }, 13 | kill: (signal: string) => { 14 | throw new WASIKillError(signal); 15 | }, 16 | randomFillSync, 17 | isTTY: () => true, 18 | path, 19 | 20 | // Let the user attach the fs at runtime 21 | fs: null, 22 | }; 23 | 24 | export default bindings; 25 | -------------------------------------------------------------------------------- /packages/wasi/src/bindings/node.ts: -------------------------------------------------------------------------------- 1 | import { randomFillSync } from "crypto"; 2 | import fs from "fs"; 3 | import { isatty as isTTY } from "tty"; 4 | import path from "path"; 5 | 6 | import { WASIBindings } from "../index"; 7 | 8 | const bindings: WASIBindings = { 9 | hrtime: process.hrtime.bigint, 10 | exit: (code: number) => { 11 | process.exit(code); 12 | }, 13 | kill: (signal: string) => { 14 | process.kill(process.pid, signal); 15 | }, 16 | randomFillSync, 17 | isTTY, 18 | fs, 19 | path, 20 | }; 21 | 22 | export default bindings; 23 | -------------------------------------------------------------------------------- /packages/wasi/test/dataview.polyfill.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BigIntPolyfill as BigInt, 3 | BigIntPolyfillType 4 | } from "../src/polyfills/bigint"; 5 | 6 | describe("dataview Polyfill", () => { 7 | DataView.prototype.setBigUint64 = undefined; 8 | DataView.prototype.getBigUint64 = undefined; 9 | let { 10 | DataViewPolyfill, 11 | DataViewPolyfillType 12 | } = require("../src/polyfills/dataview"); 13 | 14 | it("Should store and return same bigint value little-endian", async () => { 15 | let buffer = new DataViewPolyfill(new ArrayBuffer(16)); 16 | const val = BigInt(2 ** 32 + 999666); 17 | buffer.setBigUint64(0, val, true); 18 | expect(buffer.getBigUint64(0, true).toString()).toEqual(val.toString()); 19 | }); 20 | it("Should store and return same bigint value big-endian", async () => { 21 | let buffer = new DataViewPolyfill(new ArrayBuffer(16)); 22 | const val = BigInt(2 ** 32 + 999666); 23 | buffer.setBigUint64(0, val, false); 24 | expect(buffer.getBigUint64(0, false).toString()).toEqual(val.toString()); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/wasi/test/hrtime.bigint.test.ts: -------------------------------------------------------------------------------- 1 | import { BigIntPolyfillType } from "../src/polyfills/bigint"; 2 | import getBigIntHrtime from "../src/polyfills/hrtime.bigint"; 3 | 4 | let hrtime: (time?: [number, number]) => BigIntPolyfillType = getBigIntHrtime( 5 | process.hrtime 6 | ); 7 | if (process.hrtime && process.hrtime.bigint) { 8 | hrtime = process.hrtime.bigint; 9 | } 10 | 11 | const waitForTime = (milliseconds: number) => { 12 | return new Promise(resolve => { 13 | setTimeout(resolve, milliseconds); 14 | }); 15 | }; 16 | 17 | describe("hrtime Polyfill", () => { 18 | it("Should return an expected hrtime bigint value", async () => { 19 | const start: bigint = hrtime(); 20 | let diffTime: bigint = (0 as unknown) as bigint; 21 | 22 | // Wait for a second 23 | await waitForTime(1000); 24 | diffTime = hrtime() - start; 25 | expect(diffTime > 0.9e9 && diffTime < 1.4e9).toBeTruthy(); 26 | 27 | // Wait an additoonal half a second 28 | await waitForTime(500); 29 | diffTime = hrtime() - start; 30 | expect(diffTime > 1.4e9 && diffTime < 1.9e9).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES := $(shell find ./ -name '*.rs' | sed -e "s/\.rs/\.wasm/g") 2 | 3 | %.wasm: $(addprefix ./,%.rs) 4 | rustc +nightly $< --target=wasm32-wasi -o wasi_snapshot_preview1/$@ 5 | 6 | all: $(SOURCES) 7 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/args.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | let args: Vec = env::args().collect(); 5 | println!("{:?}", args); 6 | } 7 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/empty.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | } 3 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/env.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | fn main() { 4 | let existing = env::var_os("WASM_EXISTING"); 5 | println!("should be set (WASM_EXISTING): {:?}", existing); 6 | let unexisting = env::var_os("WASM_UNEXISTING"); 7 | println!("shouldn't be set (WASM_UNEXISTING): {:?}", unexisting); 8 | 9 | env::set_var("WASM_EXISTING", "NEW_VALUE"); 10 | let existing = env::var_os("WASM_EXISTING"); 11 | println!("Set existing var (WASM_EXISTING): {:?}", existing); 12 | env::set_var("WASM_UNEXISTING", "NEW_VALUE"); 13 | let unexisting = env::var_os("WASM_UNEXISTING"); 14 | println!("Set unexisting var (WASM_UNEXISTING): {:?}", unexisting); 15 | 16 | println!("All vars in env:"); 17 | for (key, value) in env::vars() { 18 | println!("{}: {}", key, value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/helloworld.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello world!"); 3 | } 4 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/sandbox_file_error.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | 4 | fn main() { 5 | let mut file = File::open("/sandbox/../outside.txt"); 6 | println!("{:?}", file.is_err()); 7 | } 8 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/sandbox_file_ok.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::prelude::*; 3 | 4 | fn main() { 5 | let mut file = File::open("/sandbox/../outside.txt"); 6 | // assert_eq!(file.is_err(), "It should return an error"); 7 | } 8 | -------------------------------------------------------------------------------- /packages/wasi/test/rs/stdin.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::io::{stdin,stdout,Write}; 3 | let mut s=String::new(); 4 | print!("Please enter some text: "); 5 | let _=stdout().flush(); 6 | stdin().read_line(&mut s).expect("Did not enter a correct string"); 7 | if let Some('\n')=s.chars().next_back() { 8 | s.pop(); 9 | } 10 | if let Some('\r')=s.chars().next_back() { 11 | s.pop(); 12 | } 13 | println!("You typed: {}",s); 14 | println!("Let's do it again!"); 15 | let mut s=String::new(); 16 | print!("Please enter some text: "); 17 | let _=stdout().flush(); 18 | stdin().read_line(&mut s).expect("Did not enter a correct string"); 19 | if let Some('\n')=s.chars().next_back() { 20 | s.pop(); 21 | } 22 | if let Some('\r')=s.chars().next_back() { 23 | s.pop(); 24 | } 25 | println!("You typed: {}",s); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /packages/wasi/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src", 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/wasm-posix/Makefile: -------------------------------------------------------------------------------- 1 | CWD:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | BUILD = ${CWD}/build 3 | DIST = ${CWD}/dist 4 | 5 | .PHONY: all 6 | all: ${DIST}/libwasmposix.a 7 | 8 | # The posix library, which is needed to run Python standalone (via wasmer), rather 9 | # than in node.js. Also, the headers are needed in order to build at all. 10 | ${DIST}/libwasmposix.a: src/wasm-posix.h src/wasm-posix.c 11 | mkdir -p ${DIST} 12 | # -w (=no warnings) since this is almost all fake/stubbed function and would generate massive warning issues: 13 | cd src/ \ 14 | && zig cc -w -target wasm32-wasi wasm-posix.c -c -o ${DIST}/wasm-posix.o 15 | cd ${DIST} && zig ar -crs libwasmposix.a wasm-posix.o 16 | rm ${DIST}/wasm-posix.o 17 | cp src/wasm-posix.h ${DIST} 18 | 19 | clean: 20 | rm -rf ${DIST} 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/wasm-posix/README.md: -------------------------------------------------------------------------------- 1 | Stub lib -------------------------------------------------------------------------------- /packages/wasmer/Makefile: -------------------------------------------------------------------------------- 1 | # See https://github.com/wasmerio/wasmer/tags 2 | VERSION = 2.1.0 3 | 4 | CWD = $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 5 | 6 | all: dist/${VERSION} 7 | 8 | dist/${VERSION}: 9 | make binary || make source 10 | 11 | # Attempt to grab a binary and install it into dist. 12 | export WASMER_DIR := ${CWD}/dist 13 | .PHONEY: binary 14 | binary: 15 | curl https://get.wasmer.io -sSfL | sh -s ${VERSION} 16 | touch dist/${VERSION} 17 | 18 | export RUSTUP_HOME := /tmp/rustup 19 | export PATH := ${HOME}/.cargo/bin:$(PATH) 20 | 21 | # Fallback if no binary available: download and build wasmer from source using Rust. 22 | # We have to do this since, e.g., there's no wasmer binary for linux-aarch64, 23 | # i.e., Docker on a macbook, and that is a required platform to support. 24 | 25 | # Another weird thing is that when we build wasmer from source it's about 26 | # 23MB, but the binary they distribute for Linux is 119MB; maybe it is 27 | # a "very fat binary". 28 | 29 | .PHONEY: source 30 | source: 31 | rm -rf dist build 32 | mkdir -p build 33 | curl https://sh.rustup.rs -sSf > build/install.sh 34 | cd build && sh ./install.sh -y 35 | cd build && git clone --depth 1 --branch ${VERSION} https://github.com/wasmerio/wasmer.git 36 | cd build/wasmer && make build-wasmer 37 | mkdir -p dist/bin 38 | cp build/wasmer/target/release/wasmer dist/bin/ 39 | rm -rf build 40 | rm -rf ${RUSTUP_HOME} 41 | touch dist/${VERSION} 42 | 43 | clean: 44 | rm -rf dist build 45 | -------------------------------------------------------------------------------- /packages/zig/Makefile: -------------------------------------------------------------------------------- 1 | # Download and install a specific tested version of zig 2 | # for your architecture here, so that we can use it for 3 | # building everything else. 4 | 5 | # Find the latest version at https://ziglang.org/download/ 6 | 7 | VERSION = 0.9.0 8 | 9 | # Using sed because uname -s --> x86_64 or arm64, but need aarch64 10 | ARCH = $(shell uname -m | sed s/arm64/aarch64/) 11 | 12 | # Using sed, because 13 | # uname -s --> Linux and Darwin 14 | # but need linux and macos 15 | OS = $(shell uname -s | sed s/Darwin/macos/ | sed s/Linux/linux/) 16 | 17 | TARBALL = https://ziglang.org/download/${VERSION}/zig-${OS}-${ARCH}-${VERSION}.tar.xz 18 | # for dev versions: 19 | #TARBALL = https://ziglang.org/builds/zig-${OS}-${ARCH}-${VERSION}.tar.xz 20 | 21 | dist/${VERSION}: 22 | rm -rf dist 23 | mkdir -p dist build 24 | curl ${TARBALL} > build/zig.tar.xz 25 | tar xf build/zig.tar.xz -C dist --strip-components=1 26 | rm build/zig.tar.xz 27 | touch dist/${VERSION} 28 | 29 | clean: 30 | rm -rf build dist -------------------------------------------------------------------------------- /packages/zlib/Makefile: -------------------------------------------------------------------------------- 1 | # See https://zlib.net/ 2 | # HORRIBLE FACT -- I found with version 1.2.11 that when zlib updated to version 1.2.12 they 3 | # just **deleted** version 1.2.11 from their download page. The result is that this Makefile 4 | # breaks. They claim to have mirrors, but it's just sourceforge only, and only has older 5 | # versions than 1.2.12. Anyway, expect this line below to -- at some completely random 6 | # moment in time -- break our build for sure, whenever they just happen to update zlib again. 7 | # It was five years from 1.2.11 to 1.2.12, so maybe that will be a while. 8 | ZLIB_VERSION = 1.2.12 9 | 10 | # https://stackoverflow.com/questions/18136918/how-to-get-current-relative-directory-of-your-makefile 11 | INIT_CWD:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 12 | DIST = ${INIT_CWD}/dist 13 | BUILD = ${INIT_CWD}/build 14 | DIST_NATIVE = ${DIST}/native 15 | DIST_WASM = ${DIST}/wasm 16 | 17 | all: native wasm 18 | 19 | ${BUILD}/zlib-${ZLIB_VERSION}.tar.xz: 20 | mkdir -p ${BUILD} 21 | cd ${BUILD} && curl https://zlib.net/zlib-${ZLIB_VERSION}.tar.xz -o zlib-${ZLIB_VERSION}.tar.xz 22 | 23 | # NATIVE 24 | 25 | ${BUILD}/native: ${BUILD}/zlib-${ZLIB_VERSION}.tar.xz 26 | rm -rf ${DIST_NATIVE} && mkdir -p ${DIST_NATIVE} 27 | cd ${BUILD} && mkdir native && tar xf zlib-${ZLIB_VERSION}.tar.xz -C native --strip-components=1 28 | 29 | ${DIST_NATIVE}/.built: ${BUILD}/native 30 | cd ${BUILD}/native && AR="zig ar" CC="zig cc" CXX="zig c++" ./configure --prefix=${DIST_NATIVE} 31 | cd ${BUILD}/native && make install -j8 32 | touch ${DIST_NATIVE}/.built 33 | 34 | .PHONY: native 35 | native: ${DIST_NATIVE}/.built 36 | 37 | 38 | # WASM 39 | 40 | ${BUILD}/wasm: ${BUILD}/zlib-${ZLIB_VERSION}.tar.xz 41 | rm -rf ${DIST_WASM} && mkdir -p ${DIST_WASM} 42 | cd ${BUILD} && mkdir wasm && tar xf zlib-${ZLIB_VERSION}.tar.xz -C wasm --strip-components=1 43 | 44 | ${DIST_WASM}/.built: ${BUILD}/wasm 45 | cd ${BUILD}/wasm && CHOST=none AR="zig ar" CC="zig cc -target wasm32-wasi-musl" CXX="zig c++ -target wasm32-wasi-musl" ./configure --prefix=${DIST_WASM} 46 | cd ${BUILD}/wasm && make install -j8 47 | touch ${DIST_WASM}/.built 48 | 49 | .PHONY: wasm 50 | wasm: ${DIST_WASM}/.built 51 | 52 | .PHONY: clean 53 | clean: 54 | rm -rf ${BUILD} ${DIST} 55 | --------------------------------------------------------------------------------