basedpl 0.1.64__tar.gz → 0.1.67__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {basedpl-0.1.64 → basedpl-0.1.67}/.cargo/config.toml +6 -5
- {basedpl-0.1.64 → basedpl-0.1.67}/.github/workflows/speed.yml +2 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/Cargo.lock +11 -11
- {basedpl-0.1.64 → basedpl-0.1.67}/Cargo.toml +10 -7
- {basedpl-0.1.64 → basedpl-0.1.67}/DEV.md +29 -23
- {basedpl-0.1.64 → basedpl-0.1.67}/PKG-INFO +1 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/exclamation.qmd +1 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/vee.qmd +2 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/explain.qmd +2 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system-functions.qmd +1 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/py/src/lib.rs +33 -28
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/__init__.py +4 -2
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/bpltests.py +119 -36
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/functions.py +6 -7
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/notebook_runner.py +7 -5
- {basedpl-0.1.64 → basedpl-0.1.67}/src/array/storage.rs +75 -75
- {basedpl-0.1.64 → basedpl-0.1.67}/src/array.rs +64 -44
- {basedpl-0.1.64 → basedpl-0.1.67}/src/batch.rs +59 -64
- {basedpl-0.1.64 → basedpl-0.1.67}/src/canvas.rs +8 -8
- {basedpl-0.1.64 → basedpl-0.1.67}/src/csv.rs +59 -58
- {basedpl-0.1.64 → basedpl-0.1.67}/src/data.rs +93 -92
- {basedpl-0.1.64 → basedpl-0.1.67}/src/date.rs +25 -25
- {basedpl-0.1.64 → basedpl-0.1.67}/src/derivative.rs +207 -212
- {basedpl-0.1.64 → basedpl-0.1.67}/src/display.rs +13 -13
- {basedpl-0.1.64 → basedpl-0.1.67}/src/distribution.rs +59 -57
- {basedpl-0.1.64 → basedpl-0.1.67}/src/error.rs +39 -25
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/assign.rs +76 -70
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/binder.rs +46 -30
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/bindings.rs +10 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/idioms.rs +22 -25
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/operators.rs +546 -539
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/session.rs +116 -85
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/tree.rs +7 -8
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval.rs +171 -119
- basedpl-0.1.67/src/execution.rs +184 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/explain.rs +20 -22
- {basedpl-0.1.64 → basedpl-0.1.67}/src/files.rs +50 -49
- {basedpl-0.1.64 → basedpl-0.1.67}/src/fusion.rs +7 -8
- {basedpl-0.1.64 → basedpl-0.1.67}/src/host.rs +15 -14
- {basedpl-0.1.64 → basedpl-0.1.67}/src/host_web.rs +5 -5
- {basedpl-0.1.64 → basedpl-0.1.67}/src/image.rs +36 -36
- {basedpl-0.1.64 → basedpl-0.1.67}/src/inspection.rs +1 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/src/js.rs +12 -12
- {basedpl-0.1.64 → basedpl-0.1.67}/src/json.rs +23 -22
- {basedpl-0.1.64 → basedpl-0.1.67}/src/lib.rs +3 -2
- {basedpl-0.1.64 → basedpl-0.1.67}/src/linear.rs +160 -160
- {basedpl-0.1.64 → basedpl-0.1.67}/src/number.rs +36 -43
- basedpl-0.1.67/src/number_theory.rs +125 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/optimize.rs +71 -78
- {basedpl-0.1.64 → basedpl-0.1.67}/src/pervasive.rs +114 -107
- {basedpl-0.1.64 → basedpl-0.1.67}/src/plot.rs +111 -111
- {basedpl-0.1.64 → basedpl-0.1.67}/src/polynomial.rs +89 -88
- {basedpl-0.1.64 → basedpl-0.1.67}/src/primitive/indexing.rs +111 -111
- {basedpl-0.1.64 → basedpl-0.1.67}/src/primitive/numeric.rs +255 -249
- {basedpl-0.1.64 → basedpl-0.1.67}/src/primitive/search.rs +94 -95
- {basedpl-0.1.64 → basedpl-0.1.67}/src/primitive/structural.rs +136 -133
- {basedpl-0.1.64 → basedpl-0.1.67}/src/primitive.rs +252 -213
- {basedpl-0.1.64 → basedpl-0.1.67}/src/protocol.rs +2 -2
- {basedpl-0.1.64 → basedpl-0.1.67}/src/reference.rs +28 -74
- {basedpl-0.1.64 → basedpl-0.1.67}/src/regex.rs +16 -15
- {basedpl-0.1.64 → basedpl-0.1.67}/src/search.rs +48 -37
- {basedpl-0.1.64 → basedpl-0.1.67}/src/selection.rs +31 -31
- {basedpl-0.1.64 → basedpl-0.1.67}/src/symbols.rs +6 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/syntax.rs +27 -28
- {basedpl-0.1.64 → basedpl-0.1.67}/src/system.rs +119 -150
- {basedpl-0.1.64 → basedpl-0.1.67}/src/timing.rs +57 -50
- {basedpl-0.1.64 → basedpl-0.1.67}/src/transform.rs +25 -26
- {basedpl-0.1.64 → basedpl-0.1.67}/src/xml.rs +37 -36
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/common/mod.rs +12 -9
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/core.rs +45 -45
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/derivatives.rs +10 -10
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/explain.rs +21 -21
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/instructions.txt +13 -13
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/README.md +3 -3
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/core.bpl +50 -11
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/distributions.bpl +11 -11
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference.rs +40 -9
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/scaling.rs +7 -6
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/settings.rs +19 -19
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/speed.rs +1 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/speed.txt +100 -102
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_api.py +1 -1
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/wasm-speed.mjs +5 -2
- basedpl-0.1.64/src/execution.rs +0 -178
- basedpl-0.1.64/src/number_theory.rs +0 -124
- {basedpl-0.1.64 → basedpl-0.1.67}/.gitattributes +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/.github/workflows/ci.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/.github/workflows/docs.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/.github/workflows/test.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/.gitignore +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/CONTRIBUTING.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/LICENSE +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/README.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/_config.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/_layouts/default.html +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/build.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/jetbrains/BPL.tmbundle/Syntaxes/bpl.tmLanguage.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/jetbrains/BPL.tmbundle/Syntaxes/hpl.tmLanguage.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/jetbrains/BPL.tmbundle/info.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/jetbrains/BPL.xml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/jetbrains/README.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-de.bundle/Contents/Info.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-de.bundle/Contents/Resources/BasedPL-de.icns +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-de.bundle/Contents/Resources/BasedPL-de.keylayout +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-de.bundle/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-de.bundle/Contents/version.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-de.txt +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-es.bundle/Contents/Info.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-es.bundle/Contents/Resources/BasedPL-es.icns +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-es.bundle/Contents/Resources/BasedPL-es.keylayout +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-es.bundle/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-es.bundle/Contents/version.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-es.txt +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-fr.bundle/Contents/Info.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-fr.bundle/Contents/Resources/BasedPL-fr.icns +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-fr.bundle/Contents/Resources/BasedPL-fr.keylayout +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-fr.bundle/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-fr.bundle/Contents/version.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-fr.txt +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-uk.bundle/Contents/Info.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-uk.bundle/Contents/Resources/BasedPL-uk.icns +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-uk.bundle/Contents/Resources/BasedPL-uk.keylayout +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-uk.bundle/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-uk.bundle/Contents/version.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-uk.txt +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-us.bundle/Contents/Info.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-us.bundle/Contents/Resources/BasedPL-us.icns +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-us.bundle/Contents/Resources/BasedPL-us.keylayout +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-us.bundle/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-us.bundle/Contents/version.plist +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/macos/BasedPL-us.txt +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/vim/ftdetect/bpl.vim +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/vim/syntax/bpl.vim +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/README.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/build.ps1 +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/install.ps1 +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/keyboard.c +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/keyboard.def +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/keyboard.rc +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/editors/windows/layout.h +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/index_files/figure-commonmark/cell-17-output-1.svg +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/index_files/figure-commonmark/cell-18-output-1.svg +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/index_files/figure-html/cell-17-output-1.svg +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/index_files/figure-html/cell-18-output-1.svg +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/LICENSE-april +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/README.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/array.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/dyalog.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/graph.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/numeric.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/power.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/regex.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/string.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/lib/tree.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/.gitignore +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/_quarto.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/arrays.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/assignment.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/bpl.xml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/bqn.xml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/bracket-types.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/cli.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/comparison.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/data.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/dfns.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/distributions.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/driver.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/dyalog.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/dyalog.xml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/evaluation.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/fonts/LICENSE-SAX2 +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/fonts/README.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/fonts/SAX2B.ttf +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/fonts/SAX2B.woff2 +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/fonts.css +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/attractor.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/boids.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/dragon.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/flow.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/mandelbrot.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/mandelbrot32.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/orbits.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/phyllotaxis.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/prefs.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery/reaction.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/gallery.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/getting-started.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/_metadata.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/alpha-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/alpha.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/at.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/backslash-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/backslash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/braces.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/brackets.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/bullet.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-backslash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-diaeresis.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-divide.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-slash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-star.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-stile.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle-times.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/circle.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/colon.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/comma-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/comma.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/del-diaeresis.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/del-stile.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/del.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/delta-stile.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/diaeresis.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/diamond-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/diamond.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/domino.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/dot.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/down-arrow.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/down-shoe.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/down-tack.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/downstile.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/epsilon-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/epsilon.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/equal-underbar-slash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/equal-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/equals.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/error-guard.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/greater-than-or-equal.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/greater-than.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/hash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/hydrant.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/infinity.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/inverted-question.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/iota-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/iota.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/jot-diaeresis.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/jot.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/lamp.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/left-arrow.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/left-shoe-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/left-shoe.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/left-tack.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/left-tail-arrow.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/less-than-or-equal.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/less-than.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/minus.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/not-equals.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/obelus.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/omega-underbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/omega.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/overbar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/parentheses.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/partial.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/pi.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/plus.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/quad-colon.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/quad-diamond.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/quad-equal.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/quad-jot.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/quad.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/question.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/quote.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/radical.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/rho.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/right-arrow-from-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/right-left-arrows.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/right-shoe.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/right-tack.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/right-tail-arrow.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/slash-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/slash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/squad.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/star-diaeresis.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/star.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/stile-tilde.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/stile.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/superscript-b.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/thorn.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/tilde-diaeresis.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/tilde.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/times.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/up-arrow.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/up-down-arrow.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/up-shoe.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/up-tack.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/upstile.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/vee-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/wedge-bar.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/wedge.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs/zilde.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs-study.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/glyphs.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/hanzi-notation-zh.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/hanzi-notation.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/hpl.xml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/index.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/indexing.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/introspection.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/keyboard.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/keyed.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/llms.txt +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/magics.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/modules.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/nbdev.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/notebooks.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/numbers.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/overview.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/papers/aplwiki-arrays.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/papers/brickman-depth-1-scalars.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/papers/mercer-based-system.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground/bpl.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground/gallery.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground/hpl.tmLanguage.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground/monaco-worker.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground/page.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground/worker.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/playground.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/plot.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/principles.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/processes.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/puppy.jpg +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/python.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/regex.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/repl.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/scripts.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/sidebar.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/strings.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/01_basics.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/02_functions.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/03_operators.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/04_nested-arrays.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/05_searching-indexing.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/06_rearranging-arrays.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/study/07_numerical-tools.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/style.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/styles.css +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/syntax.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/_metadata.yml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/c.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/canvas.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/constants.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/copy.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/csv.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/date.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/decompose.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/deflate.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/delay.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/detach.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/distribution.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/edge.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/element.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/ex.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/fetch.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/float.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/hash.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/host.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/image.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/index.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/js.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/json.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/literal.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/load.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/memory.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/metadata.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/mime.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/mkdir.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/nc.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/nget.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/nl.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/normalize.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/nput.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/path.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/plot.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/prefs.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/r.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/rand.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/readdir.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/remove.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/rename.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/signal.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/src.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/storage.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/svg.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/time.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/ucs.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/uuid.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/vfi.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/system/xml.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/terms.qmd +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/web.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/nbs/xml.ipynb +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/package-lock.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/package.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/README.md +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/aplcart.jsonl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/april.jsonl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/bqn.jsonl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/core.jsonl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/dyalog.jsonl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/provenance/ngn.jsonl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/py/Cargo.toml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/py/build.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/pyproject.toml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/_modidx.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/bpl.tmLanguage.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/dyalog.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/editors.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/font.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/fonts.css +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/hpl.tmLanguage.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/input.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/ipython.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/keyboards.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/keyboards.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/layout.json +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/lb.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/notebooks.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/printing.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/reference.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/syntax.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/windows_keyboard.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/python/basedpl/worker.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/rustfmt.toml +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/scripts/install-macos.sh +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/scripts/prep.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/agreement.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/array/text.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/canvas.js +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/element.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/eval/dissect.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/fusion/blocks.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/image/codecs.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/image/imageio.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/keyed.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/name.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/src/settings.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/conftest.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/highlighting.mjs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/input.mjs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/LICENSE-aplcart +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/LICENSE-april +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/LICENSE-bqn +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/LICENSE-bqncrate +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/LICENSE-dyalog +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/LICENSE-ngn +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/aplcart.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/april.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/bqn.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/dyalog.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/graphics.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/lib.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/ngn.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/regex.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/static-int32.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/static.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/support/april.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/support/avl.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/support/redblack.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/support/sbst.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/reference/support/splay.bpl +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_basic.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_bpltests.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_glyphs.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_graphics.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_instructions.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_ipython.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_kernel.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_keylayout.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_notebooks.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_reference.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_repl.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_windows_keyboard.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/test_worker.py +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/units.rs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/wasm-pkg.mjs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/wasm.mjs +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/tests/windows_keyboard.c +0 -0
- {basedpl-0.1.64 → basedpl-0.1.67}/wheel/data/share/jupyter/kernels/bpl/kernel.json +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
# rustc runs LLVM's straight-line (SLP) vectorizer only at opt-level 3. `passes
|
|
2
|
-
#
|
|
1
|
+
# rustc runs LLVM's straight-line (SLP) vectorizer only at opt-level 3. `passes` adds it to our opt-level 2 builds.
|
|
2
|
+
# With LTO, rustc runs these passes before the link step, and the loop vectorizer only at it. `loop-vectorize` runs first. Otherwise SLP packs the lanes of a short inner loop, and the loop vectorizer then leaves the outer loop alone.
|
|
3
|
+
# A target's own `rustflags` replace these. The x86-64 and wasm entries repeat the passes.
|
|
3
4
|
[build]
|
|
4
|
-
rustflags = ["-C", "passes=slp-vectorizer"]
|
|
5
|
+
rustflags = ["-C", "passes=loop-vectorize,slp-vectorizer"]
|
|
5
6
|
|
|
6
7
|
# rustymath uses Accelerate's per-thread BLAS controls on macOS.
|
|
7
8
|
[env]
|
|
@@ -9,11 +10,11 @@ MACOSX_DEPLOYMENT_TARGET = "15.0"
|
|
|
9
10
|
|
|
10
11
|
# x86-64 builds need a CPU with AVX2 (x86-64-v3). The kernels rely on its rounding and wider vector instructions.
|
|
11
12
|
[target.'cfg(target_arch = "x86_64")']
|
|
12
|
-
rustflags = ["-C", "target-cpu=x86-64-v3", "-C", "passes=slp-vectorizer"]
|
|
13
|
+
rustflags = ["-C", "target-cpu=x86-64-v3", "-C", "passes=loop-vectorize,slp-vectorizer"]
|
|
13
14
|
|
|
14
15
|
# The browser build uses wasm's 128-bit SIMD, which current browsers all support.
|
|
15
16
|
[target.wasm32-unknown-unknown]
|
|
16
|
-
rustflags = ["-C", "target-feature=+simd128", "-C", "passes=slp-vectorizer"]
|
|
17
|
+
rustflags = ["-C", "target-feature=+simd128", "-C", "passes=loop-vectorize,slp-vectorizer"]
|
|
17
18
|
|
|
18
19
|
[alias]
|
|
19
20
|
# Builds the npm package into `wasm/pkg/`. `xtask` builds optimised, because its wasm-bindgen pass runs on every call.
|
|
@@ -18,6 +18,8 @@ jobs:
|
|
|
18
18
|
- run: pip install 'fastws-cli>=0.0.25' && cargo stage --profile dist
|
|
19
19
|
- run: grep -m1 'model name' /proc/cpuinfo
|
|
20
20
|
- run: wheel/scripts/bpl --costs tests/speed.txt
|
|
21
|
+
# `bpl-tests thread_scaling` times a large product, a 512×512 SVD and Hermitian eigendecomposition, a stack of QR decompositions and a large elementwise function on one thread and on the runner's cores, and fails when one speeds up less than 1.5 times. Speed budgets are single-thread measurements, so this is the check of multithreaded speed. It comes last, so that a failure here still leaves every cost logged.
|
|
22
|
+
- run: pip install -e . && bpl-tests thread_scaling --bpl wheel/scripts/bpl
|
|
21
23
|
|
|
22
24
|
wasm:
|
|
23
25
|
runs-on: ubicloud-standard-4
|
|
@@ -93,7 +93,7 @@ checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
|
|
|
93
93
|
|
|
94
94
|
[[package]]
|
|
95
95
|
name = "basedpl"
|
|
96
|
-
version = "0.1.
|
|
96
|
+
version = "0.1.67"
|
|
97
97
|
dependencies = [
|
|
98
98
|
"base64 0.23.1",
|
|
99
99
|
"bytemuck",
|
|
@@ -137,7 +137,7 @@ dependencies = [
|
|
|
137
137
|
|
|
138
138
|
[[package]]
|
|
139
139
|
name = "basedpl-cli"
|
|
140
|
-
version = "0.1.
|
|
140
|
+
version = "0.1.67"
|
|
141
141
|
dependencies = [
|
|
142
142
|
"async-trait",
|
|
143
143
|
"basedpl",
|
|
@@ -150,7 +150,7 @@ dependencies = [
|
|
|
150
150
|
|
|
151
151
|
[[package]]
|
|
152
152
|
name = "basedpl-py"
|
|
153
|
-
version = "0.1.
|
|
153
|
+
version = "0.1.67"
|
|
154
154
|
dependencies = [
|
|
155
155
|
"basedpl",
|
|
156
156
|
"num-bigint",
|
|
@@ -165,7 +165,7 @@ dependencies = [
|
|
|
165
165
|
|
|
166
166
|
[[package]]
|
|
167
167
|
name = "basedpl-wasm"
|
|
168
|
-
version = "0.1.
|
|
168
|
+
version = "0.1.67"
|
|
169
169
|
dependencies = [
|
|
170
170
|
"basedpl",
|
|
171
171
|
"js-sys",
|
|
@@ -867,9 +867,9 @@ dependencies = [
|
|
|
867
867
|
|
|
868
868
|
[[package]]
|
|
869
869
|
name = "kernmini"
|
|
870
|
-
version = "0.1.
|
|
870
|
+
version = "0.1.21"
|
|
871
871
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
872
|
-
checksum = "
|
|
872
|
+
checksum = "9d391ffc3e7d540ca47206110b11ad619095d1fb8911604ca2abc1e43fb2c488"
|
|
873
873
|
dependencies = [
|
|
874
874
|
"async-trait",
|
|
875
875
|
"bytes",
|
|
@@ -1413,9 +1413,9 @@ dependencies = [
|
|
|
1413
1413
|
|
|
1414
1414
|
[[package]]
|
|
1415
1415
|
name = "rustymath"
|
|
1416
|
-
version = "0.1.
|
|
1416
|
+
version = "0.1.43"
|
|
1417
1417
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1418
|
-
checksum = "
|
|
1418
|
+
checksum = "47219acf94cb55feca56bf23818d8d1c013a17fd63023721e6edac118c065629"
|
|
1419
1419
|
dependencies = [
|
|
1420
1420
|
"foldhash",
|
|
1421
1421
|
"half",
|
|
@@ -1434,9 +1434,9 @@ dependencies = [
|
|
|
1434
1434
|
|
|
1435
1435
|
[[package]]
|
|
1436
1436
|
name = "rustymath-buffer"
|
|
1437
|
-
version = "0.1.
|
|
1437
|
+
version = "0.1.43"
|
|
1438
1438
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1439
|
-
checksum = "
|
|
1439
|
+
checksum = "6991cc5072aadb4af88d88526fb6479f59f9641b86fd6f10b7a407b8e02bd6e4"
|
|
1440
1440
|
dependencies = [
|
|
1441
1441
|
"pyo3",
|
|
1442
1442
|
"rustymath",
|
|
@@ -2086,7 +2086,7 @@ checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
|
|
|
2086
2086
|
|
|
2087
2087
|
[[package]]
|
|
2088
2088
|
name = "xtask"
|
|
2089
|
-
version = "0.1.
|
|
2089
|
+
version = "0.1.67"
|
|
2090
2090
|
dependencies = [
|
|
2091
2091
|
"anyhow",
|
|
2092
2092
|
"rustymath",
|
|
@@ -5,14 +5,14 @@ members = ["py"]
|
|
|
5
5
|
join_comments = true
|
|
6
6
|
|
|
7
7
|
[workspace.package]
|
|
8
|
-
version = "0.1.
|
|
8
|
+
version = "0.1.67"
|
|
9
9
|
edition = "2024"
|
|
10
10
|
rust-version = "1.98"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
|
|
13
13
|
[workspace.dependencies]
|
|
14
|
-
rustymath = { version = ">=0.1.
|
|
15
|
-
rustymath-buffer = ">=0.1.
|
|
14
|
+
rustymath = { version = ">=0.1.43, <1", default-features = false }
|
|
15
|
+
rustymath-buffer = ">=0.1.43, <1"
|
|
16
16
|
pyo3 = ">=0.29.2, <1"
|
|
17
17
|
pyo3-build-config = ">=0.29.2, <1"
|
|
18
18
|
|
|
@@ -34,8 +34,8 @@ inherits = "release"
|
|
|
34
34
|
|
|
35
35
|
[profile.release]
|
|
36
36
|
opt-level = 2
|
|
37
|
-
#
|
|
38
|
-
lto =
|
|
37
|
+
# Thin LTO across the crate's own codegen units, Cargo's default. Incremental builds put `#[inline]` helpers such as iterator adapters in codegen units of their own, where only LTO can inline them.
|
|
38
|
+
lto = false
|
|
39
39
|
overflow-checks = false
|
|
40
40
|
debug-assertions = false
|
|
41
41
|
incremental = true
|
|
@@ -45,10 +45,13 @@ debug = false
|
|
|
45
45
|
[profile.dist]
|
|
46
46
|
inherits = "release"
|
|
47
47
|
incremental = false
|
|
48
|
-
# Cargo's default LTO: thin LTO across the crate's codegen units, which inlines between them.
|
|
49
|
-
lto = false
|
|
50
48
|
strip = true
|
|
51
49
|
|
|
50
|
+
# A local run before each release (`cargo test --profile checked`): debug assertions add `session()`'s turn check and the standard library's checks in unsafe code, at 12 to 55% per call.
|
|
51
|
+
[profile.checked]
|
|
52
|
+
inherits = "test"
|
|
53
|
+
debug-assertions = true
|
|
54
|
+
|
|
52
55
|
[features]
|
|
53
56
|
default = ["native"]
|
|
54
57
|
# rustymath's own linear algebra, beside Accelerate on macOS, for `RUSTYMATH_NATIVE=1`. The Mac release builds turn it off.
|
|
@@ -26,17 +26,17 @@ When another agent's unfinished edits stop the shared tree compiling, run `cargo
|
|
|
26
26
|
|
|
27
27
|
An already-running Python process retains its loaded extension. In a kernel, use `from basedpl.worker import fresh; fresh(code)` to check the installed `bpl` in a fresh worker instead of restarting the kernel. A BPL error raises `BplError`. `fresh(code, bpl=PATH)` runs a saved copy of `bpl`, such as the baseline described below. Check one expression and read its result before building on it. `basedpl.bpltests.check_file` and `check_page` check reference files and documentation through the worker without building Rust. With `bpl=PATH`, they check a saved copy without installing it.
|
|
28
28
|
|
|
29
|
-
Measure with `BASEDPL_COSTS=1 cargo test --test speed` or `basedpl.bpltests.time_cases(cases)`. Both count costs in copies. A copy is the time to write as many floats as the data has items, each negated, into a second buffer. Budgets are single-thread measurements.
|
|
29
|
+
Measure with `BASEDPL_COSTS=1 cargo test --test speed` or `basedpl.bpltests.time_cases(cases)`. Both count costs in copies. A copy is the time to write as many floats as the data has items, each negated, into a second buffer, with both in cache. `rustymath::timing::COPIES` stores the copy time of each machine that runs the speed tests, by its CPU, the item's size and the number of items, at the slower end of what that copy normally takes. A copy timed beside each case varied more than 3 times on CI's runner, where the functions' own times didn't, so stored times keep that noise out of every cost. On a machine or a size with no entry, the test measures the copy once and prints a line to add to `COPIES`. Each case's line in a log ends with its time for each item, and the log has a copy line for each size of data, which `logged_costs` reads into its result's `copies`. Budgets are single-thread measurements. `thread_scaling()` checks multithreaded speed: it times a large product, a 512×512 SVD and Hermitian eigendecomposition, a stack of QR decompositions and a large elementwise function on one thread and on the machine's cores, and fails when one speeds up less than 1.5 times. Accelerate's GESDD and SYEVD run on one thread whatever its threading setting. On macOS, where Accelerate computes the product, the SVD and the Hermitian eigendecomposition, those three cases need only the speedups in `_ACCELERATED_LEAST`. The manual `Speed` workflow runs it. `speed_costs()` reads the cases in `tests/speed.txt`. `cases=` selects expressions, `wasm=DIR` selects a browser build, `bpl=PATH` selects another native `bpl`, such as a saved copy, and `native=True` times rustymath's own linear algebra, which dev builds compile in. `logged_costs(log)` reads CI output from its text or a file. `cargo_costs(path)` runs either project's Rust speed test, and `pytest_costs(path)` runs rustymath's Python speed test.
|
|
30
30
|
|
|
31
|
-
Costs in copies can't show a per-call change of a few percent, because code layout alone moves a timing by more than that. `instructions(cases)` counts instructions, which load and layout don't change. Each case is BPL that makes one call per step of `N`, such as `{⍵}⍣N⊢0`. A map such as `f¨N⍴0` calls a dfn once on all its cells, so it gives no per-call cost. It runs fresh single-threaded `bpl` processes at four values of `N`. For each pair of successive values, it divides the difference in instructions retired by the difference in `N`, which cancels startup and `setup=`, and it reports the median of those three slopes. The median drops a one-off cost that starts once `N` passes some size, because such a cost falls between only one pair. `≢•ucs N⍴"ab" "cde"` has one: it retires about 5×10⁶ more instructions from about 131,350 items on. Every value of `N` lies past the warm-up, from a rough count at 1,000 steps, because a case that allocates an array at each step retires extra instructions over its first steps while the allocator settles. Each process pays a different warm-up, up to about 5×10⁶ instructions, so successive values of `N` lie 5×10⁸ instructions apart, where that difference moves a slope by under 1%. `counts=` sets two or more values instead. It reads `/usr/bin/time -l` on macOS and `perf stat` on Linux, and `bpl=PATH` counts a saved copy.
|
|
31
|
+
Costs in copies can't show a per-call change of a few percent, because code layout alone moves a timing by more than that. `instructions(cases)` counts instructions, which load and layout don't change. Each case is BPL that makes one call per step of `N`, such as `{⍵}⍣N⊢0`. A map such as `f¨N⍴0` calls a dfn once on all its cells, so it gives no per-call cost. It runs fresh single-threaded `bpl` processes at four values of `N`. For each pair of successive values, it divides the difference in instructions retired by the difference in `N`, which cancels startup and `setup=`, and it reports the median of those three slopes. The median drops a one-off cost that starts once `N` passes some size, because such a cost falls between only one pair. `≢•ucs N⍴"ab" "cde"` has one: it retires about 5×10⁶ more instructions from about 131,350 items on. Every value of `N` lies past the warm-up, from a rough count at 1,000 steps, because a case that allocates an array at each step retires extra instructions over its first steps while the allocator settles. Each process pays a different warm-up, up to about 5×10⁶ instructions, so successive values of `N` lie 5×10⁸ instructions apart, where that difference moves a slope by under 1%. `counts=` sets two or more values instead. It reads `/usr/bin/time -l` on macOS and `perf stat` on Linux, and `bpl=PATH` counts a saved copy. A case that only drops a structural result that stays pending, such as large windows or a transpose, counts making the pending copy, not the copy: `{⍵⊣3↕v}⍣N⊢0` retires about 15,000 instructions a step. An indexed write computes the whole array first. `{x←3↕v ⋄ x.[0]←0 ⋄ ⍵}⍣N⊢0` counts the copy itself, about 12 million instructions a step. A join computes at once and needs no write.
|
|
32
32
|
|
|
33
33
|
`tests/instructions.txt` holds the instructions that each case retires for each step, as counted on macOS on Apple silicon. Most cases make one call per step, from `{⍵}⍣N⊢0` to `fib`. Three map a function over the same 64 cells each step, through Each, Rank's general path and a system function's own item map. `instruction_budgets()` counts each case through `instructions` and marks a case `over` when it retires more than 3% above its count, and `stale` when it retires more than 3% below it. `tests/test_instructions.py` fails on either mark, and skips on other platforms. The change that makes a call cheaper also lowers each count it moves. A new compiler version can inline differently and move the counts, and then they're set again from `bpl-tests instruction_budgets`. CI checks only the speed budgets.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
`profile(case)` shows where a case's instructions go, by function, on macOS. A fresh process repeats the case on one thread while `sample` counts how often each function is at the top of the main thread's stack. A function's share of those samples, times the build's count from `instructions`, estimates its instructions per step. The estimate assumes every function retires instructions at the same rate. When a count moves, `profile_diff(case, base)` shows which functions moved it, from those estimates for both builds. A function that one build inlines and the other calls out of line appears under one build alone. Run them before reading machine code. `base` is a saved copy, and `bpl=PATH` sets the build to profile, the installed one by default.
|
|
36
36
|
|
|
37
|
-
`bpl-tests NAME ARGUMENT...` runs one of these functions from the shell and prints its result: `check_file`, `check_page`, `time_cases`, `instructions`, `instruction_budgets`, `profile_diff`, `speed_costs`, `cargo_costs`, `pytest_costs` or `wasm_test`. Run long checks and timings this way, not in the shared kernel, which runs one cell at a time for every agent. For example, `bpl-tests time_cases '+/v' --threads 4` times `+/v` on four threads. `time_cases`, `instructions` and `speed_costs` take cases as positional arguments. When a case starts with `-`, put the cases last, after `--`, as in `bpl-tests speed_costs -- '-.×m' '+/v'`. `bpl-tests NAME -h` lists a function's arguments.
|
|
37
|
+
`bpl-tests NAME ARGUMENT...` runs one of these functions from the shell and prints its result: `check_file`, `check_page`, `time_cases`, `instructions`, `instruction_budgets`, `profile`, `profile_diff`, `speed_costs`, `thread_scaling`, `cargo_costs`, `pytest_costs`, `compare_costs` or `wasm_test`. Run long checks and timings this way, not in the shared kernel, which runs one cell at a time for every agent. For example, `bpl-tests time_cases '+/v' --threads 4` times `+/v` on four threads. `time_cases`, `instructions` and `speed_costs` take cases as positional arguments. When a case starts with `-`, put the cases last, after `--`, as in `bpl-tests speed_costs -- '-.×m' '+/v'`. `bpl-tests NAME -h` lists a function's arguments.
|
|
38
38
|
|
|
39
|
-
On each push, the `Test` workflow logs every native cost in its `test` job and every wasm cost in its `wasm` job. The speed test that writes them times the test-profile build that the budgets limit. `bpl --costs FILE` times the cases in a file in `speed.txt`'s format as the speed test does, and prints each cost with this machine's budget, without failing on a budget. The manual `Speed` workflow runs it on a `dist` build, the profile releases use, on CI's runner, beside its wasm timing. The `Release` workflow runs it on each binary it builds, on GitHub's runners. Their logs show what releases cost, for comparison with the `Test` workflow's. `logged_costs` reads all three workflows' logs. When you review a run, look for costs over their budgets and for costs that changed by more than 20
|
|
39
|
+
On each push, the `Test` workflow logs every native cost in its `test` job and every wasm cost in its `wasm` job. The speed test that writes them times the test-profile build that the budgets limit. `bpl --costs FILE` times the cases in a file in `speed.txt`'s format as the speed test does, and prints each cost with this machine's budget, without failing on a budget. The manual `Speed` workflow runs it on a `dist` build, the profile releases use, on CI's runner, beside its wasm timing. The `Release` workflow runs it on each binary it builds, on GitHub's runners. Their logs show what releases cost, for comparison with the `Test` workflow's. `logged_costs` reads all three workflows' logs. When you review a run, look for costs over their budgets and for costs that changed by more than 20% since the run before. `compare_costs(before, after)` lists both, from two runs' logs or from the rows that `speed_costs` and the other timing functions give. A review needs each job's full log, because `ghapi`'s `failed_step_log` leaves out the step that prints the CPU model. Study a slowdown until you know its cause only when it appears in more than one CI run, or when two or more similar cases slow by similar amounts. Treat any other slowdown as normal variation.
|
|
40
40
|
|
|
41
41
|
An initial budget is twice what a first-principles analysis says the case should cost, as each budget file's header says. A later budget comes from a fuller understanding of the case than that analysis gives, such as a platform difference or a cost the analysis missed, and the case's comment records it. A case over its budget needs study, which finds either a speed bug to fix or something the analysis missed. Avoid raising a budget to match a run without understanding why the case costs what it does. Each case line in a budget file holds a native budget for macOS on Apple silicon, then one for Linux on CI's x86 runner, then, in basedpl's file, a wasm budget. One expected cost serves every platform at first, until a specific difference on one platform is found. macOS running matrix products and decompositions on Accelerate, where CI runs rustymath's own kernels, is such a difference. A comment above each case, or above its group, gives the expected cost with its reason, and any platform's own cost with the reason for it. `set_budgets(expected)` writes the budgets for the cases that `expected` names, a dict from each case's code to its expected cost. `builds=` limits the change to one platform's column, for a cost that differs there. A case can appear in more than one section, such as `[copies]` and `[widths]`. `sections=` limits the change to the sections it names. `path=` selects one of rustymath's budget files, `tests/speed.txt` or `tests/speed_python.txt`.
|
|
42
42
|
|
|
@@ -44,11 +44,11 @@ An initial budget is twice what a first-principles analysis says the case should
|
|
|
44
44
|
|
|
45
45
|
The speed tests and the scaling test time each failing case again after the rest, after each pause in `rustymath::timing::RETRY_PAUSES`, and fail only for a case that fails every time. Each retry prints the case's earlier and new costs, even in a passing run. `logged_costs` keeps a retried case's lowest cost. Study a case that needs a retry in most runs, as you would a slowdown.
|
|
46
46
|
|
|
47
|
-
For a change meant to make something faster, or a change to code that every call goes through, time the cases it affects before and after the change. Before your first `cargo develop`, copy the installed `bpl` in the kernel with `shutil.copy(basedpl.worker.INSTALLED_BPL, '/tmp/<prefix>_bpl')`. Time that copy with `bpl='/tmp/<prefix>_bpl'`, and your build with the default. Another agent's `cargo develop` can replace the installed `bpl` at any time. The copy keeps your baseline fixed while agents build in parallel.
|
|
47
|
+
For a change meant to make something faster, or a change to code that every call goes through, time the cases it affects before and after the change. Before your first `cargo develop`, copy the installed `bpl` in the kernel with `shutil.copy(basedpl.worker.INSTALLED_BPL, '/tmp/<prefix>_bpl')`. Time that copy with `bpl='/tmp/<prefix>_bpl'`, and your build with the default. Another agent's `cargo develop` can replace the installed `bpl` at any time. The copy keeps your baseline fixed while agents build in parallel. Compare the two builds' times per item, which `time_cases` prints in picoseconds. A build from before a change to `rustymath::timing::COPIES` divides its costs by a different copy. On this Mac, a fused case over a large input can cost up to about 15% more in one process than in another, depending on whether `malloc` starts the input on a 32 KB boundary, where every one of a graph's blocks then ends. Time both builds the same way, each alone in a fresh process or at the same place in one run, and alternate them before you read a lone difference of that size as a regression. The speed test fails a case only past its budget. It misses any smaller slowdown. Other changes need only the speed test. Don't restore old code in the shared tree to get a baseline. On macOS, native timings of matrix products and decompositions mostly measure Accelerate. Rustymath's DEV.md says how to time rustymath's own kernels.
|
|
48
48
|
|
|
49
|
-
Cooperating tools share the machine's timing lock, `/tmp/aai-ws-timing.lock`. Timings take it exclusively. `cargo develop`, `cargo wait`, `cargo wasm`, the reference test, pytest and `wasm_test` without `speed` take it shared. Rust code takes it through `rustymath::timing`. Python code takes it with `fastws.cargo.timing_lock`. Node can't lock a file. `wasm_test` and `speed_costs(wasm=DIR)` hold the lock in Python while Node runs. Other Rust tests and bare Cargo commands do not all take it. After five minutes a timing fails. A build or test run then goes ahead without the lock. After 30 seconds, an exclusive wait prints the processes with the lock file open. The list can include processes that are only waiting for the lock. Never take the lock yourself in the shared kernel process. Never coordinate timings by hand. A hold that outlasts its call blocks the timings that later calls launch. Each such call stalls every other agent's kernel calls while it waits. Native timings run on one thread and request user-interactive macOS QoS, which favours the performance cores. QoS is not CPU affinity.
|
|
49
|
+
Cooperating tools share the machine's timing lock, `/tmp/aai-ws-timing.lock`. Timings take it exclusively. `cargo develop`, `cargo wait`, `cargo wasm`, the reference test, pytest, `check_file`, `check_page`, `bpl-nb` and `wasm_test` without `speed` take it shared. Rust code takes it through `rustymath::timing`. Python code takes it with `fastws.cargo.timing_lock`. Node can't lock a file. `wasm_test` and `speed_costs(wasm=DIR)` hold the lock in Python while Node runs. Other Rust tests and bare Cargo commands do not all take it. After five minutes a timing fails. A build or test run then goes ahead without the lock. After 30 seconds, an exclusive wait prints the processes with the lock file open. The list can include processes that are only waiting for the lock. Never take the lock yourself in the shared kernel process. Never coordinate timings by hand. A hold that outlasts its call blocks the timings that later calls launch. Each such call stalls every other agent's kernel calls while it waits. Native timings run on one thread and request user-interactive macOS QoS, which favours the performance cores. QoS is not CPU affinity.
|
|
50
50
|
|
|
51
|
-
Run `cargo wasm` during development only for browser work. Agents use `cargo wasm --out DIR`, then run the browser tests against that build with `basedpl.bpltests.wasm_test(DIR)` for `tests/wasm.mjs` and `wasm_test(DIR, speed=True)` for `tests/wasm-speed.mjs`. This avoids replacing a module another agent is using. For other edits under `cfg(web)` or `host_web.rs`, check compilation with `cargo
|
|
51
|
+
Run `cargo wasm` during development only for browser work. Agents use `cargo wasm --out DIR`, then run the browser tests against that build with `basedpl.bpltests.wasm_test(DIR)` for `tests/wasm.mjs` and `wasm_test(DIR, speed=True)` for `tests/wasm-speed.mjs`. This avoids replacing a module another agent is using. For other edits under `cfg(web)` or `host_web.rs`, check compilation with `cargo cs -p basedpl-wasm --target wasm32-unknown-unknown`, which checks the browser wrapper crate together with basedpl's browser code. `cargo cs` alone compiles neither.
|
|
52
52
|
|
|
53
53
|
### X86-64 machine code
|
|
54
54
|
|
|
@@ -74,7 +74,7 @@ Read the newest `lib<crate>-<hash>.rlib` under `/tmp/aai-ws-x86/x86_64-unknown-l
|
|
|
74
74
|
|
|
75
75
|
The default workspace members are core, CLI and Python; wasm/xtask build only when requested. Frontends use the core's public API. Core/CLI Rust tests live in `tests/` and `cli/tests/`. Libraries set `test = false` to avoid separate `cfg(test)` builds. Because no build runs `#[cfg(test)]` code, libraries hold no inline tests or test-only counters. Any internal behaviour worth a test, such as whether an expression fuses, is worth knowing for every user too. Tests in `tests/` check it through the public API, such as `•explain`, `•storage` or `•memory`. PyO3 is in its own crate, not an optional core feature. The core uses `serde_json` for the process protocol and data functions.
|
|
76
76
|
|
|
77
|
-
Rust 1.98 or later supplies algebraic float operations. `test` inherits `release`: opt-level 2, incremental compilation, LTO
|
|
77
|
+
Rust 1.98 or later supplies algebraic float operations. `test` inherits `release`: opt-level 2, incremental compilation, thin LTO across each crate's codegen units, no debug information, overflow checks or debug assertions. A `debug_assert!` therefore never runs here. Write a check that matters as an always-on test. `.cargo/config.toml` adds LLVM's loop and SLP vectorizers as extra passes, in that order, and targets x86-64-v3 on x86. Target-specific rustflags repeat the passes because they replace build-wide flags. `dist` disables incremental compilation and strips symbols. Keep these settings aligned with rustymath's.
|
|
78
78
|
|
|
79
79
|
`fastws`'s `cargo develop` builds profile `test`, installs the extension and stages `bpl` into the venv. `py/` enables PyO3's `extension-module` and supplies its link arguments without libpython. Editable installs use `fastws.build_backend`; they install metadata and a path to `python/` without compiling Rust. After a standalone `uv sync` or editable pip install, run `cargo develop`. `ws-sync` performs that rebuild after syncing. Wheels use maturin for the extension and `cargo stage` for the Python-free CLI. Linux staging runs inside the manylinux container.
|
|
80
80
|
|
|
@@ -88,11 +88,13 @@ Use published dependency ranges with the lower bounds required by the APIs in us
|
|
|
88
88
|
|
|
89
89
|
### Codegen and rebuilds
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Local builds of the `test` profile are incremental, with thin LTO across each crate's codegen units (Cargo's default, `lto = false`). Incremental builds put `#[inline]` helpers and generic instances, such as iterator adapters, `array::map` and `copy_from_slice`, in codegen units of their own, where only LTO can inline them. Measured on 22 October 2026, thin LTO cut per-call instruction counts by 4 to 18%, to within about 3% of `dist`. A one-line edit in basedpl then rebuilt `bpl` in about 9 to 12 s, against 6 to 8 s without LTO, and an edit in rustymath took about 30 s either way. Thin LTO imports a function into another unit, where it can inline it, only below LLVM's import limit of 100 instructions. A long fused iterator chain can exceed that limit. rustymath's DEV.md lists what else stays out of line, under "Loops and codegen". Under LTO, rustc runs the extra `passes` from `.cargo/config.toml` before the link step, and the loop vectorizer only at that step. The configs list `loop-vectorize` before `slp-vectorizer` for that reason. Listed alone, SLP packed the lanes of short inner loops before the loop vectorizer could vectorize those loops across items. `∪c` then ran 71% slower, in `dist` builds too. After an interrupted build, a link error on undefined `.llvm.<hash>` symbols means incremental compilation reused stale codegen units. `cargo clean -p <crate> --profile test` clears them.
|
|
92
|
+
|
|
93
|
+
Moving lines changes the code below them, because each panic site, such as a bounds check, holds its line. Every unit that holds that code then recompiles. Related source is split into child modules to limit those shifts. For example, `fusion/blocks.rs` holds the generic block runner of `fusion.rs`. After a one-line edit to `Graph::run`, the rebuild recompiles one unit of about 140 KB and takes about 2.4 s. It took about 9 s when the runner sat below it in the same file. Children use `use super::*` and expose items to parents and siblings with `pub(super)`. Parents own public re-exports.
|
|
92
94
|
|
|
93
95
|
Generic loop instances can concentrate in the defining module's codegen unit. A closure passed to a generic adapter such as `map` makes an instance that can compile in another codegen unit. Every edit to the closure's function then recompiles that unit. When `Graph::run` built a vector with `map`, an edit to it recompiled a 1.9 MB unit in about 8 s. It now uses a plain loop. Mark loop helpers `#[inline]` when their instances need to compile with the caller's kernel table, including nested helpers. Kernel tables have separate modules: `booleans`, `wholes`, `float_kernels` and `complex_kernels`. `at_widths!` gives the concrete widths their own modules. Keep new tables separate and add kernel specializations only for measured gains.
|
|
94
96
|
|
|
95
|
-
Use `cargo build --lib --bins --profile test --timings` to inspect the normal build. Its report under `target/cargo-timings/` separates frontend and codegen time. `BASEDPL_UNITS=1 cargo test --test units -- --nocapture` lists large units and their functions. `BASEDPL_SIZES=pattern cargo test --test units -- --nocapture` measures machine code matching a demangled-name regex. `BASEDPL_SIZES` sees only basedpl's own codegen units. `BASEDPL_SIZES_DIFF=old:new cargo test --test units machine_code_growth -- --nocapture` compares two binaries, such as copies of `bpl` from before and after a change, by generic function across every crate. `BASEDPL_CALLS=pattern cargo test --test units -- --nocapture` lists each call from a function whose demangled name matches to a function in another codegen unit, in basedpl's build and in the rustymath rlib it links.
|
|
97
|
+
Use `cargo build --lib --bins --profile test --timings` to inspect the normal build. Its report under `target/cargo-timings/` separates frontend and codegen time. `BASEDPL_UNITS=1 cargo test --test units -- --nocapture` lists large units and their functions. `BASEDPL_SIZES=pattern cargo test --test units -- --nocapture` measures machine code matching a demangled-name regex. `BASEDPL_SIZES` sees only basedpl's own codegen units. `BASEDPL_SIZES_DIFF=old:new cargo test --test units machine_code_growth -- --nocapture` compares two binaries, such as copies of `bpl` from before and after a change, by generic function across every crate. `BASEDPL_CALLS=pattern cargo test --test units -- --nocapture` lists each call from a function whose demangled name matches to a function in another codegen unit, in basedpl's build and in the rustymath rlib it links. Those calls stayed out of line through thin LTO. rustymath's DEV.md says which calls do, under "Loops and codegen". After an edit, `BASEDPL_RECOMPILED=pattern cargo test --test units -- --nocapture` lists the units that the rebuild compiled again, each with its biggest functions whose demangled names match. These commands diagnose the current compilation arrangement; don't create scratch builds to approximate it.
|
|
96
98
|
|
|
97
99
|
## Code map
|
|
98
100
|
|
|
@@ -122,7 +124,7 @@ User-facing language rules belong in `nbs/syntax.qmd`, `evaluation.qmd`, `number
|
|
|
122
124
|
|
|
123
125
|
The lexer/parser records byte spans and structural syntax without evaluating expressions. `parse` distinguishes complete input, incomplete input and errors. It reads numbers under the current settings, which are process-wide (`settings.rs`). `Session::parse` passes the parser the `hanzi_aliases` setting. `syntax_tree` supplies source tools with structural nodes and recovers invalid chunks as `Error` nodes. `highlight` shares the lexer.
|
|
124
126
|
|
|
125
|
-
`symbols.rs` owns APL, simplified and traditional Hanzi spellings. Its rows also hold each glyph's names: a glyph name for the character, which is also its page's file name in `nbs/glyphs/`, and a name for each valence, or one name where both valences do the same thing. The glyph reference, named entry
|
|
127
|
+
`symbols.rs` owns APL, simplified and traditional Hanzi spellings. Its rows also hold each glyph's names: a glyph name for the character, which is also its page's file name in `nbs/glyphs/`, and a name for each valence, or one name where both valences do the same thing. The glyph reference, named entry and help read them there. Python's exported names read them, with each system function's name, through `symbols::builtins`. `hanzi_aliases` enables Hanzi glyph spellings, canonicalizing token decisions while retaining original source and spans. Other Hanzi remain name characters. `_` quotes the next non-whitespace character into a name, including a glyph alias. The `notation` preference affects structured function/operator display, not stored source, serialization or structural APIs; help and editor metadata use the same spelling table.
|
|
126
128
|
|
|
127
129
|
The binder resolves runtime categories right to left. It owns unfinished strands, trains and bound arguments; shared function nodes are immutable. Grammatical reduction produces application requests instead of calling functions. An array that holds a function as an item has its own binder category, `Callable` (`Category::bound`), while `Category::of`, which assignment's grammar reads, keeps it `Value`. `Binder::push` works out each entity's role once, as it pushes it: `Category::before` resolves `Callable` against the role of the entity below, which is to its right, to `Function` where that entity is a subject or an absent `⍺`, and to `Value` otherwise. `Rule::get` and the strengths don't change, and recordings check `Callable` as a category of its own. A function-only operand slot converts such an array to `FunctionNode::Array` in `constant_operand`, and `Function::from_value` makes the same conversion where a call by text needs a function. Primitive/operator rows own names, operand kinds, ranks, pervasion, axis behaviour and reduction identities. Change those rows instead of duplicating metadata in consumers. A non-extending primitive above its natural ranks runs through Rank; pervasive Each/Outer/Rank paths reuse whole-array kernels.
|
|
128
130
|
|
|
@@ -148,7 +150,7 @@ Tail calls reuse frames while preserving lexical dependencies. Handlers disable
|
|
|
148
150
|
|
|
149
151
|
## Values, layouts and assignment
|
|
150
152
|
|
|
151
|
-
`Value` holds a number, character, function, operator or shared array. Compact storage holds a rustymath `buffer::Buffer` of Booleans, narrow integers, width-specific floats or complex numbers, or characters, all of one dtype, and `Items` borrows it as a `buffer::View`. BPL's promotion rules for those dtypes stay in basedpl, as `Promotion` in `array/storage.rs`. A rustymath buffer keeps the dtype it's given, and basedpl picks an integer's width from its values. Mixed storage holds `Value`s and memoizes its prototype, its innermost lexical dependency and whether any item is a function. Other properties are computed when needed. An array holds its layout and its storage in one allocation. Its `Shape` holds ranks 0 to 2 inline, so a small array allocates no shape. Constructors take `impl Into<Shape>`: pass a slice, or an array such as `[n]`, rather than building a `Vec`. A shape, key or name change on a shared array makes a view of the base array's storage and copies no items. A large result of a fusible float call holds a pending call in place of its storage, and computes the storage on the first read (`fusion.rs`). `Value::view` and `imported_buffer` are the public width-preserving frontend boundary. They give and take rustymath's `View` and `Buffer`, which basedpl's API re-exports as `basedpl::buffer`.
|
|
153
|
+
`Value` holds a number, character, function, operator or shared array. Compact storage holds a rustymath `buffer::Buffer` of Booleans, narrow integers, width-specific floats or complex numbers, or characters, all of one dtype, and `Items` borrows it as a `buffer::View`. BPL's promotion rules for those dtypes stay in basedpl, as `Promotion` in `array/storage.rs`. A rustymath buffer keeps the dtype it's given, and basedpl picks an integer's width from its values. Mixed storage holds `Value`s and memoizes its prototype, its innermost lexical dependency and whether any item is a function. Other properties are computed when needed. An array holds its layout and its storage in one allocation. Its `Shape` holds ranks 0 to 2 inline, so a small array allocates no shape. Constructors take `impl Into<Shape>`: pass a slice, or an array such as `[n]`, rather than building a `Vec`. A shape, key or name change on a shared array makes a view of the base array's storage and copies no items. A part of compact storage, such as one of Rank's cells or a partition (`Value::part`), is a view of a range of its base's items. A view keeps its whole base alive, as a NumPy view does. A part of mixed storage is a copy, because the memo answers for all of a mixed array's items. A large result of a fusible float call holds a pending call in place of its storage, and computes the storage on the first read (`fusion.rs`). `Value::view` and `imported_buffer` are the public width-preserving frontend boundary. They give and take rustymath's `View` and `Buffer`, which basedpl's API re-exports as `basedpl::buffer`.
|
|
152
154
|
|
|
153
155
|
Atoms, rank-0 arrays and singleton vectors are distinct. Function nodes retain handles, not lexical frames or Python objects. Functions match by their parts; dfns and generators match by identity. Operators can be values and can cross the native Python boundary. Values and function nodes are `Send + Sync`.
|
|
154
156
|
|
|
@@ -160,11 +162,11 @@ Numeric storage fills with zero and characters with blank. Mixed nonempty arrays
|
|
|
160
162
|
|
|
161
163
|
Under with a function and selective assignment find their targets through labels. Each begins a label level (`transform::Kind::Label`) and runs the selector, or evaluates the target, on the root wrapped as a `selection::Labelled` value. A primitive whose row selects carries the labels through its own code. Planned forms and `⌷` compose a rustymath `Mapping` relative to the root, so a selection reads only the items it selects. Ordering forms select through `Primitive::taken`. Any other call strips the labels and runs on the values. `Labels::read` takes the labels of the selector's result. Labels that are still a mapping with no fill write back through that mapping, as a walk over the root (`Targets::Walk`). `Labels::replacements` turns any other labels into offsets, or into paths where the root has nested items. A strand of labelled items joins into one labelled array (`Carrier::joins_at_once`). An item that the selector makes rather than selects has the `FOREIGN` label. Writing to it is a DOMAIN ERROR. A result without labels at the level sends Under to the selector's inverse. A batched try refuses while a label level runs (`Levels::admits_batch`). Function-Under applies its selector to a left argument as a plain value. Positions-Under passes the left argument whole. Keyed values align to selected keys, and nested paths retain empty-cell structure.
|
|
162
164
|
|
|
163
|
-
Selective assignment removes the binding before writing. `Arc::make_mut` copies only when another holder needs the old array. Repeated targets must read values written by earlier occurrences. `pervasive::scatter` combines each target of `+ - × ÷ ⌈ ⌊` into compact numbers in place, in turn, at the kind that the call computes in, through rustymath's `
|
|
165
|
+
Selective assignment removes the binding before writing. `Arc::make_mut` copies only when another holder needs the old array. Repeated targets must read values written by earlier occurrences. `pervasive::scatter` combines each target of `+ - × ÷ ⌈ ⌊` into compact numbers in place, in turn, at the kind that the call computes in, through rustymath's `gather::Writes`, whose `combine` and `try_combine` read a selection's walk a run of offsets at a time, without listing it. Other pervasive modifiers group repeated targets into rounds of distinct positions. A target at a fill reads the fill, and its write is dropped. Append updates use `Value::append` when layouts permit it; rank/key/name changes fall back to ordinary catenation. Modified destructuring calls modifiers left to right; ordinary destructuring assigns right to left. An assignment's RHS runs once.
|
|
164
166
|
|
|
165
167
|
Each, Rank and outer products call their operand through `each_pair` in `eval/operators.rs`. It first tries one batched call (`batch::tried`), then calls the operand once for each cell and assembles the results through shared layout and prototype rules. A 0-cell preserves an atom or encloses an array item. For custom operands, empty Each, Rank and outer product calls substitute prototypes for empty arguments and use prototype mode. Explicit effects remain observable when a call runs. Catchable prototype-call failures produce prototype zero, but interrupt, timeout and unsupported errors propagate. Restore prototype mode on success and error. A stencil runs as Rank over `↕`'s windows, and reads its specification through `↕`'s `WindowSpec` (`primitive/structural.rs`).
|
|
166
168
|
|
|
167
|
-
Rank tries `whole` before `each_pair`. `whole` calls the operand once where neither argument has a frame. It then tries `decomposed`, for `•decompose⍤2` on a stack of matrices, and `whole_cells` for a primitive or a product. `whole_cells` holds Rank's routes for whole arguments, which batched calls share (`batch::whole_call`). In order, they are `primitive::stacked`, which runs `⌹` and `-.×` on stacks of inexact matrices through rustymath's `linalg::Stacked`, as rustymath's Python API runs them, and every `f.g` that rustymath has a product kernel for, on compact stacks of any item kind, through `linalg::Product` (an exact product that doesn't fit 64 bits keeps the call for each cell, which computes exactly), one padded call of a pervasive primitive (`padded_call`), `primitive_cells` for a primitive with one argument or with a left argument that every cell shares, and `paired_cells` for a primitive whose arguments' frames match or whose right argument every cell shares. An argument with no axes besides its cells' is the same in every cell. After `whole_cells`, `whole` tries `pervasive_cells`, for a pervasive primitive under nested Rank, and then, for a monadic operand, `cell_rule`, which also takes folds, atops and a bound left argument. `primitive_cells` runs a primitive on the whole argument along the cells' axes. A form that `extends` runs whole at a cell rank at least its own. Other forms follow the `cells` fact (`CellRule`) in their row of `Primitive::info()`. With it, `⊖ , ↑ ↓ ⌷ #` and rotate-first call their axis forms, `⍉` sends each cell's axes after the frame's, `≢` and `⍴` give constants,
|
|
169
|
+
Rank tries `whole` before `each_pair`. `whole` calls the operand once where neither argument has a frame. It then tries `decomposed`, for `•decompose⍤2` on a stack of matrices, which factors the whole stack through rustymath's `linalg::Stacked`, as `•decompose` factors one matrix, and `whole_cells` for a primitive or a product. `whole_cells` holds Rank's routes for whole arguments, which batched calls share (`batch::whole_call`). In order, they are `primitive::stacked`, which runs `⌹` and `-.×` on stacks of inexact matrices through rustymath's `linalg::Stacked`, as rustymath's Python API runs them, and every `f.g` that rustymath has a product kernel for, on compact stacks of any item kind, through `linalg::Product` (an exact product that doesn't fit 64 bits keeps the call for each cell, which computes exactly), one padded call of a pervasive primitive (`padded_call`), `primitive_cells` for a primitive with one argument or with a left argument that every cell shares, and `paired_cells` for a primitive whose arguments' frames match or whose right argument every cell shares. An argument with no axes besides its cells' is the same in every cell. After `whole_cells`, `whole` tries `pervasive_cells`, for a pervasive primitive under nested Rank, and then, for a monadic operand, `cell_rule`, which also takes folds, atops and a bound left argument. `primitive_cells` runs a primitive on the whole argument along the cells' axes. A form that `extends` runs whole at a cell rank at least its own. Other forms follow the `cells` fact (`CellRule`) in their row of `Primitive::info()`. With it, `⊖ , ↑ ↓ ⌷ #` and rotate-first call their axis forms, `⍉` sends each cell's axes after the frame's, `≢` and `⍴` give constants, a reshape that keeps a cell's items in order is a view, and `⊂` encloses each cell of rank 1 or more through its axis form, whose cells are views of the argument's items. Any other form with a left argument that every cell shares takes `paired_cells`. `paired_cells` joins each pair of cells for `,` and `⍪`, and calls a form that extends once where both cells are at least its ranks. It first repeats an argument that every cell shares over the frame (`agreement::spread`). A pervasive primitive folds or scans a simple array along the cell's axis (`folded_cells`). `f/∘,` folds all the cell axes at once. An atop applies its inner function's rule, then Rank for its outer function. Each rule applies only where Rank's mix of the cells' results gives what the whole-array call gives. Other nested results, and folds by other functions, keep the call for each cell.
|
|
168
170
|
|
|
169
171
|
Axis nodes select/move axes and reuse Rank when the function has no direct axis meaning. Vector-axis reduction uses the general composed-ravel rule, preserving axis order and the whole seed. Single-axis reductions retain direct kernels. Layout-only changes share storage. Structural results retain renderers where their rank permits; scans and in-place changes copy complete layouts. Match ignores renderers.
|
|
170
172
|
|
|
@@ -190,7 +192,7 @@ Settings live once for the whole process, in `settings.rs`, and are read from th
|
|
|
190
192
|
|
|
191
193
|
Only f64 comparisons use the session tolerance, from 0 to `1E-14`. f32/f16 compare exactly except fixed-point convergence, which uses four ulps; f64 convergence uses `1E-14` independently of the preference. Index/count/Boolean/character-offset conversion uses `near_integer` with the fixed default tolerance, not the comparison preference. The tolerant types and objects come from rustymath. Keep independent inside/outside-tolerance tests with exact/mixed controls; don't use language comparison as its own oracle.
|
|
192
194
|
|
|
193
|
-
`search.rs` selects compact exact or tolerant backends. Exact integer/Boolean/character storage uses keys; float searches use their width. Integer haystacks accept the whole-number shortcut only for exactly whole float needles. Tolerant indexing validates whole-key matches unless the haystack is exactly whole and falls back to buckets on mismatch. Classification intentionally chains tolerance through sorted runs, so it need not match every member to one representative. Other cells use exact hashes or first-number buckets with `Value::matches` confirmation, falling back to pairwise comparison where indexing is unsuitable. Grade is stable. Position buffers use u32 and become integer storage without another pass. `⍷` and monadic Key take each class's positions from `search::class_positions`, which keeps a class's own buffer for 32-bit positions and copies narrower ones once from the shared buffer. Dyadic Key selects rows through `
|
|
195
|
+
`search.rs` selects compact exact or tolerant backends. Exact integer/Boolean/character storage uses keys; float searches use their width. Integer haystacks accept the whole-number shortcut only for exactly whole float needles. Tolerant indexing validates whole-key matches unless the haystack is exactly whole and falls back to buckets on mismatch. Classification intentionally chains tolerance through sorted runs, so it need not match every member to one representative. Other cells use exact hashes or first-number buckets with `Value::matches` confirmation, falling back to pairwise comparison where indexing is unsuitable. Grade is stable. Position buffers use u32 and become integer storage without another pass. `⍷` and monadic Key take each class's positions from `search::class_positions`, which keeps a class's own buffer for 32-bit positions and copies narrower ones once from the shared buffer. Dyadic Key selects rows through `Groups::each` slices. Key's group-fold idiom folds through `search::numbered`'s class numbers. Where `fold_groups` declines, Key builds the positions from those numbers. Key with the operand `≢⊢`, which `≢∘⊢` also builds, counts its classes through `search::class_counts` and builds no positions.
|
|
194
196
|
|
|
195
197
|
`Domain` and `with_field!` select working reals, Complex64 or BigRational for generic numeric algorithms. Compact storage selects the domain without scanning its items. `number::read` converts once; `Field::array` builds compact float/complex results without a `Number` per item. Each caller lists the domains it supports. Compact complex input does not take a real path merely because all imaginary parts are zero; results built through `Value::complex` can normalize to float storage.
|
|
196
198
|
|
|
@@ -214,7 +216,7 @@ A failed compact kernel leaves placeholder results and triggers a wider/exact/co
|
|
|
214
216
|
|
|
215
217
|
`fusion.rs` defers eligible float calls: `Primitive::pervasive_mapped` gives a pending array (`Data::Node` holding a `fusion::Pending`) in place of the result. Pending arrays survive assignment and link to the pending arrays they read. Reading one forces it through `ArrayData::storage`, which builds a `Graph` from the pending calls that only that graph reads and runs it block by block through the same pervasive kernels. Readers that need only the shape, kind or prototype, such as `Value::len` and `Value::environment`, answer without forcing. `Value::force` forces a result, as timing does. Each node writes a block to a buffer that later nodes reuse once its readers have run. A comparison that a later call in the graph reads writes 1 and 0 as floats there (`Dyad::compared`, rustymath's `tolerant::Indicator`). `⍚` and `@[p]` run cases that can't fail on every item and select their results by a Boolean mask (`selected` in `eval/operators.rs`). `fusion::select` defers the select where the mask is such a comparison, and otherwise runs it at once through rustymath's `chosen`. Small graphs run unfused unless a supported reduction consumes them directly. Node-making code stays out of line from dispatch, because inlined code grows the frame that stays on the stack at every recursion level. The module doc owns eligibility rules and limits; don't copy its thresholds here. A structural copy of one source through rustymath `Steps` (`remapped`) whose result has at least `MIN_ITEMS` items gives a pending copy (`fusion::copy`) for any compact kind. Its map is a `Mapping::Steps` with one entry for each result axis. Broadcasting's maps use the same variant. Forcing composes a read of a copy with the copy's map, and moves a read with no repeat or fill under the pervasive calls it reaches (`Pending::resolved`), so `2×⌽v+1` reads `v` backwards in one graph. A read that copies (rustymath's `Mapping::copies`) moves only under a call that reads one array. A call whose two array arguments copy arrays of one layout through one map runs on those arrays, and the map then copies its result once (`fusion::copied_once`). `Graph::block` shrinks a graph's blocks where its buffers wouldn't fit in the cache. Rustymath's `agreement::expanded` reads a forward run in place, and copies anything else through `gather::walk_range`. A copy that no call reads through is walked as `remapped` walks it. Fused and ordinary folds share rustymath's `reduce::along`/`fold_into` traversal. A fold of a copy of windows along its own axis slides over the copy's source instead (`fusion::slide`, through rustymath's `reduce::sliding`). Under `∂`, `fold_rule` takes a maximum's or minimum's positions in such windows from the source (`fusion::picks`).
|
|
216
218
|
|
|
217
|
-
Ordinary pervasive kernels also read pending copies through their maps. `fusion::source` snapshots the source and mapping without taking the pending work, and rustymath composes that mapping with the call's agreement. `pervasive::Input` keeps the snapshot alive while kernels read its storage through existing blocked readers. Folds by rustymath's reductions read a pending copy through `fusion::source` too (`pervasive::mapped_fold`). `reduce::along_mapped` folds a copy that reads a contiguous block of its source with its axes in another order, as `Mapping::block` finds it, in the block's own order. Transposes, reversals and windows that don't overlap read such blocks. Where the totals come out in another order, `pervasive::fold` makes them a copy through the tables that `along_mapped` gives, and the next fold reads that copy as a block again. It reads any other copy block by block. A read of a few items from a pending array goes through `fusion::narrow`, which narrows the pending work to the positions read without consuming it. A chain of copies becomes a read of its source, for any item kind. A call or graph that nothing else holds becomes one over only those positions, when the read takes at most 1 in 128 of its items. Where maps don't compose, as a rotation's doesn't with
|
|
219
|
+
Ordinary pervasive kernels also read pending copies through their maps. `fusion::source` snapshots the source and mapping without taking the pending work, and rustymath composes that mapping with the call's agreement. `pervasive::Input` keeps the snapshot alive while kernels read its storage through existing blocked readers. Folds by rustymath's reductions read a pending copy through `fusion::source` too (`pervasive::mapped_fold`). `reduce::along_mapped` folds a copy that reads a contiguous block of its source with its axes in another order, as `Mapping::block` finds it, in the block's own order. Transposes, reversals and windows that don't overlap read such blocks. Where the totals come out in another order, `pervasive::fold` makes them a copy through the tables that `along_mapped` gives, and the next fold reads that copy as a block again. It reads any other copy block by block. A read of a few items from a pending array goes through `fusion::narrow`, which narrows the pending work to the positions read without consuming it. A chain of copies becomes a read of its source, for any item kind. A call or graph that nothing else holds becomes one over only those positions, when the read takes at most 1 in 128 of its items. Where maps don't compose, as a rotation's doesn't with an overtake when it carries source items across the end, such a read lists its items' offsets. A pending array that a name holds is computed whole on its first read and keeps its items, so a loop of single reads doesn't compute it again. `fusion::item` serves single items and picks, and `Gather::walk` serves small copies and gathers through `fusion::walk_through`. Checks of a whole input's values, such as bounds and finiteness, read only the items that its logical `ArrayView` selects. Unsupported compositions and dense readers retain the pending array's one cached compact result.
|
|
218
220
|
|
|
219
221
|
`explain.rs` holds the events that `•explain` reports. One check in `Function::reported` serves both `•prefs ["fusion":$t]` and `•explain`: a call that computes a pending argument writes the pref's line and records a `forced` event. `Graph::explained` records a fused group where a graph runs, and where a fold reads through one. `Idiom::call` records each fast combination taken, by the name `Idiom::name` gives. `linear::scatter_add`, and the transpose of a gather that scatters a source's items, record each accumulation. `Selection::write_into`, where assignment and Under write, records each write that leaves a compact array mixed. Fusion runs graphs without a session, so events go to a sink on the evaluating thread, which holds them only while `•explain` runs. Outside it, `call` pays one branch on the session's flags. Each record node and pending array holds an `explain::Live` guard, which counts it for the thread that made it until it is dropped, on any thread. `•memory` reports those counts, and `Value::footprint` walks a value's allocations for its `bytes`.
|
|
220
222
|
|
|
@@ -222,9 +224,9 @@ Ordinary pervasive kernels also read pending copies through their maps. `fusion:
|
|
|
222
224
|
|
|
223
225
|
`batch.rs` makes a map's batched call. `batch::tried`, which `each_pair` calls, begins a batched level for the run (`transform::Kind::Batched`), wraps each argument that has a frame in a `Batch` carrier at that level, calls the operand once, and ends the level. Each map first lays its arguments over its frame through its own agreement, the pairing that its calls on each cell read (`batch::Arguments::laid` and `agreement::laid_cells`). `each_pair` makes no batched call for one cell of atoms, which runs faster without one. Routing hands each call whose newest level is the run's to `batch::call`: a primitive, an operation that primitives name, such as a fold by a primitive or `+.×`, or an operation by a dfn. It leaves `⍕` and random draws to their own code. It runs any other system function once for each cell, on that cell's arguments, as the function runs once for each item of an argument that holds several, and joins the cells' results into one batch (`batch::system`, `batch::each_cell`). `batch::call` runs the call on the whole arguments by Rank's routes: `eval::whole_cells` through `batch::whole_call`, and `eval::folded_cells` for a fold. A primitive therefore batches wherever Rank runs it on whole arguments. A fold or scan that `folded_cells` doesn't cover, such as one by a dfn, runs the fold's own loop over the batch's items. Each stage is then one call, on item `j` of every cell (`Batch::at`). `fold_array` and `primitive_fold` join a result that holds stage batches into one batch (`joined`). Every fold and scan route decides its structural cases, such as a unit, empty lanes and the result's shape, through `FoldPlan`, and ends in `folded`, which alone gives the result its layout, fused folds included. A fold or scan by any function other than a primitive, over more than one lane, also tries a batched run of its own: `operators::by_lanes` moves the fold axis last and runs the derived fold over the lanes through `batch::tried`. `operators::by_triples` does the same for `f.g` where `g` isn't a primitive: one batched call of `g` on a frame of rows by contraction by columns, then the fold by `f` along the contraction, for each chunk of rows whose triples hold at most `batch::CHUNK` items (`operators::by_rows`, which products of primitives share). Each runs its usual loop from the start where its try gives `None`. A fold keeps its stops on its operand's `Definition` (`Function::remembers`). `batch::tries` holds the conditions for any try. A call that no route covers stops the run (`transform::Stop`). A call whose constants with axes, repeated over the frame, would hold more than 4 times the items of its arguments and more than `batch::CHUNK` items, 2²¹, stops the run with the cells of a chunk whose share holds at most `CHUNK` items (`oversized`, `Stop::chunk`). `tried` then runs the operand once on each chunk of that many cells (`batch::chunked`), each as a batched level on a frame of one axis, and joins the chunks' results as the run over cells assembles its cells' results. A chunk that stops for too many items again starts the chunks over with its own smaller chunk. Where one cell's share alone passes `CHUNK`, the map calls its operand once for each cell. Code that must run once for each cell asks the levels first (`Levels::effect`): output, input, `⍕` of a carried value, and the code in a system function that acts (`Effect::Acts`, through `Context::acts`), a guard or a call on prototypes that would catch an error (`Effect::Catches`), and a write to a binding outside the map's frames (`Effect::Writes`, through `Levels::record_write`). A read through `Value::observed`, as a count or a position makes, stops the run too. A guard whose predicate is a batch of the run splits the run instead (`Session::guard` through `operators::guard`). `batch::truths` gives each cell's Boolean, and each side runs once, as a guard case on its own cells (`Levels::case`), through the `Cells` that `⍚` uses. Each batched binding in the frames that the run started is read at the side's cells (`Session::restricted`, through `batch::read_cells` and `operators::cells_at`), and `Cells::assembled` joins the sides' results. During a case, the run's frame is the case's cells, a random draw draws for the case's cells, and a write to an enclosing frame stops the run. A guard of a dfn that already splits the cells in another frame stops the run as a recursive call (`Cause::Recursion`), and sides whose cells differ in layout or kind stop it as a call with no rule. Each stop carries its cause (`transform::Cause`), which says how long a dfn operand remembers it (`Cause::kept`). An effect, a write, an observing read or a recursive call lasts for every later call. A call that no route covers lasts while later calls repeat the arguments' kinds and cell shapes, and the memory guard lasts while they also repeat the frame's shape. A caught error never lasts, because whether an error arises depends on the cells' values. The dfn's `Definition` holds what it remembers (`Definition::remember`): `ALWAYS`, or a signature of the arguments (`batch::signature`), each holding the stop's cause (`batch::holding`). A later try that it covers falls back without calling the operand, with an `"an earlier stop"` event that names the cause. A stop's own `fallbacks` event names its cause (`Cause::reason`), and the glyph of a primitive whose call no rule covers (`Stop::refusing`). Code with no rule for batches that reads a batch's items gets item carriers, item i of every cell, and records an `itemwise` row, route `"batched items"`. `explain::itemwise` is the one helper for routes that handle items one at a time, and the innermost call claims its counts (`Function::reported`), as `forced` does. A kernel that then reads a number meets the stop that `Carrier::computed` gives a batch. A missed reader therefore costs speed or a fallback, never a wrong result. A batched run can also start inside a `∂` call. The run's level is newer than the `∂` tag, so the batch rules take each call first, and their calls on whole arrays reach the derivative rules. A batch's whole can then carry a dual, whose kind batch checks read through `Value::template_dtype`. Stacks of matrices whose wholes carry a dual take the derivative rules through `Pairing::Matrices` (`eval::whole_cells`), because the stacked kernels read compact storage. A `∂` call inside the run differentiates with respect to batches of atoms or of arrays. `derivative::numbers` counts a cell's numbers from the batch's template, every cell shares each direction's seed, and the Jacobian joins into one batch (`batch::joined`). A move with no plan on a dual whose primal is a batch runs on the primal and the tangent apart (`derivative::apart`). Adjoints that a transpose adds into a batch add as one sum on its whole (`batch::scatter_add`). An attached rule runs on the whole batch at its argument's `∂` tag (`batch::call`), and `batch::split_whole` and `batch::dual_of_wholes` keep the newest level outermost. A roll draws once, on its argument laid over the frame (`distribution::drawn_items`), and deals and samples draw for each cell in turn (`distribution::each_cell`), all from one stream in cell order. Where each cell draws once, seeded draws match a run cell by cell, except under a guard that splits the cells, where each side draws for its own cells in turn. A try that falls back puts back the state of each generator it drew from (`distribution::settle`). The map's result must be a batch at its level, a strand of batches whose kinds join (`joined`), or an atom or compact array with no carrier, as every cell's result. Any other result falls back. A pervasive call stops where its result's kind differs from the kind that one route gives every item (`pervasive::expected`). Each cell then keeps the kind that a run cell by cell gives it. `reference::check` runs each reference case with the `batch` pref off, then on, then on under fusion's small sizes (`fusion::Sizes::SMALL`), and compares the storage kinds of the later runs' values with the first's.
|
|
224
226
|
|
|
225
|
-
Rustymath owns numeric folds, scans, adjacent pairs, grouped reductions and integer bounds. Basedpl selects kernels, tolerance, seeds, storage widths and exact fallbacks. Which reduction folds each primitive comes from rustymath's `facts!` table, through `pervasive::folds` and `truth_fold`, and so
|
|
227
|
+
Rustymath owns numeric folds, scans, adjacent pairs, grouped reductions and integer bounds. Basedpl selects kernels, tolerance, seeds, storage widths and exact fallbacks. Which reduction folds each primitive comes from rustymath's `facts!` table, through `pervasive::folds` and `truth_fold`, and so do the object by whose rule each takes characters and the comparison objects that `with_comparison!` makes. Folds by algebraic float operations can regroup. Contiguous subtraction/division folds use `Alternating`; quotients fall back to item-by-item folding if their products leave the normal range. Boolean scans compose truth-table maps; comparison scans compute both possible totals in bulk and use `reduce::chosen`. Seeds, axis lanes and scalar fallback preserve the language's accumulation direction. Generic reductions stay right-associated and preserve operand call order/effects. Monadic `f.g` is the exception: it memoizes each minor's expansion, and a cached minor does not repeat operand calls or their effects. Determinant/permanent fast paths use the appropriate rustymath backend.
|
|
226
228
|
|
|
227
|
-
The automatic Rayon scheduler serves rustymath elementwise `many` and `pairs`, broadcasts through `agreement::blocks`, folds through `reduce::along`, fused graphs and matrix products. Scans don't split. Decompositions split in the products of their blocked updates, the passes of their Hermitian and bidiagonal reductions, and their divide-and-conquer merges. `rank` sends dyadic `+.×` and `⌹`, monadic `⌹` and `-.×`, and `•decompose` over the 2-cells of an unkeyed stack with compact storage to `primitive::stacked` and `stacked_decompositions`. They run each cell through the rustymath function it uses alone. A pervasive operand, or Rank nested down to one, runs as one call: each argument gets length-1 axes after each level's frame, so leading-axis agreement pairs frames with frames and cells with cells. Arguments with axis names still go cell by cell. Rustymath's `parallel::
|
|
229
|
+
The automatic Rayon scheduler serves rustymath elementwise `many` and `pairs`, broadcasts through `agreement::blocks`, folds through `reduce::along`, fused graphs and matrix products. Scans don't split. Decompositions split in the products of their blocked updates, the passes of their Hermitian and bidiagonal reductions, and their divide-and-conquer merges. `rank` sends dyadic `+.×` and `⌹`, monadic `⌹` and `-.×`, and `•decompose` over the 2-cells of an unkeyed stack with compact storage to `primitive::stacked` and `stacked_decompositions`. They run each cell through the rustymath function it uses alone. A pervasive operand, or Rank nested down to one, runs as one call: each argument gets length-1 axes after each level's frame, so leading-axis agreement pairs frames with frames and cells with cells. Arguments with axis names still go cell by cell. Rustymath's `parallel::stack` splits whole cells across threads in rounds, with rustymath's interrupt check between rounds. A caller whose checks run far apart calls `check_now`, because `Context::check` reads the clock only on every 64th call. The scheduler adapts to call cost, uses `RAYON_NUM_THREADS` by default and accepts one override for the whole process through `parallel::set_threads` (0 default, 1 serial), which `•prefs ["threads":n]` writes directly. The timing harness sets one thread after each reset of the settings. Nested Rayon work does not split again; wasm is serial. A fused graph splits in pieces of whole blocks: an elementwise graph through `parallel::extended`, and a folded one through `reduce::fold_pieces`. On one thread an elementwise graph appends straight to its result, because copying each block into a piece's slots cost up to 1.2 copies there. Small converted chunks limit the work available to the scheduler. Don't infer multicore execution from a large array alone.
|
|
228
230
|
|
|
229
231
|
A pair that rustymath has a product kernel for, such as `+.×`, `⌈.+`, `∨.∧` or `+.=`, runs through `linalg::Product::run`, which Python's `inner` also takes (`pervasive::known_product`). Other native pairs use its bounded `inner_with` runner and `reduce::right_fold`, carrying the right tail across blocks. Basedpl supplies its primitive kernels, tolerance and exact-widening fallback. An integer product that doesn't fit 64 bits gives way to the exact path. `pervasive::inner` takes each operand's `Orientation`. Its float and complex products read a transposed operand in place, as reverse mode's `product_transpose` needs. A float `+.×` whose operand is a pending copy, or a view of one, hands rustymath's `Inner::inner_mapped` the copy's source and map (`fusion::source`, `pervasive::mapped_product`). Rustymath gives gemm an operand that its map reads at strides, such as a transpose, where it lies (`Mapped::matrix`). It reads any other copy a block of rows at a time, except in a product with one column, where the copy's rows are adjacent in storage, as for a vector or a single-channel image. It then adds each column of the copy where it lies (`linalg::by_columns`). Neither builds the copy. A copy that a name holds is read again for each product rather than cached. Reverse mode transposes a product whose operand is windows that move by 1 as one product: the windows of the zero-padded cotangent with the kernel reversed (`linear::convolved_back`, rustymath's `signal::correlation`). Structural gather/compress and interval index also reuse rustymath. Measure changes to generic or backend code in both projects. The callers' codegen and layout decide costs that a kernel measured alone misses.
|
|
230
232
|
|
|
@@ -232,9 +234,11 @@ A pair that rustymath has a product kernel for, such as `+.×`, `⌈.+`, `∨.
|
|
|
232
234
|
|
|
233
235
|
`eval_with`/`call_with` install `EvalOptions`. Echo defaults to true; disabling it suppresses implicit display, not explicit output or display commands. Output is ordered `Output { kind, data }` MIME bundles. An optional sink receives events as produced; otherwise the evaluation retains them. Values, functions, operators, outputs and errors remain separate results.
|
|
234
236
|
|
|
235
|
-
`
|
|
237
|
+
`Execution::begin` puts each evaluation's interrupt handle, deadline and poll in `execution::STOPS`, a static that every thread reads. Its `Running` guard restores the stops of any evaluation it ran inside, such as the one that called `⍎`. `execution::stopped` reads them, and the first evaluation installs it as rustymath's interrupt check (`rustymath::interrupt`). Rustymath's long computations, such as exact matrix functions, primes and stacks of matrices, then stop for basedpl's interrupts and deadlines, on its worker threads as well. A thread without a turn, such as the one Python's `in` runs on, also stops at an interrupt of the running evaluation.
|
|
238
|
+
|
|
239
|
+
Evaluation code reads the process's one session through `session()`, and takes a `&Span` where it reports errors. Checks at interpreter boundaries and in long loops call `execution::check`, which counts down a static before it reads clocks and flags or calls the poll hook. It takes no span and no session, and code without a turn, such as Python's `in`, calls it too. Individual BigInt operations and bounded native kernels are not preempted. The session's state sits in `Cell`s and `RefCell`s whose methods hand out owned values, so no caller holds a borrow of it while a dfn evaluates (`meta/session.md`). An optional `Input` supplies `⎕` and `•nget "-"`; without one they raise IO. Python uses cooperative cancellation. The process worker can kill an unresponsive process after a grace period, losing its state; requests are never replayed.
|
|
236
240
|
|
|
237
|
-
`Session::call` resolves and calls a function expression on existing arrays without temporary argument bindings. Retained functions use `call_function_with`.
|
|
241
|
+
`Session::call` resolves and calls a function expression on existing arrays without temporary argument bindings. Retained functions use `call_function_with`. `Function::call` gives each error that a function's code raised without a span the call's span. The evaluator gives its own errors the spans of the nodes they report, and a statement gives any error still without a span its own. An error raised outside an evaluation, as by Python's indexing, has no span. Diagnostics display with Unicode width. Python translates spans to character offsets; process JSON keeps bytes. Native function/operator handles cross the in-process Python boundary, not process JSON.
|
|
238
242
|
|
|
239
243
|
### Python
|
|
240
244
|
|
|
@@ -270,7 +274,7 @@ The playground and gallery load released assets from jsDelivr, or local `nbs/pla
|
|
|
270
274
|
|
|
271
275
|
## System functions and rendering
|
|
272
276
|
|
|
273
|
-
`system.rs` owns the system table: one `SystemFunction` for each system function, holding its case-insensitive name, call, valence,
|
|
277
|
+
`system.rs` owns the system table: one `SystemFunction` for each system function, holding its case-insensitive name, call, valence, extension, result prototype and inverse. Its extension (`primitive::Extension`) is the field that each primitive's form holds too. A system function takes its right argument whole, or takes items (`primitive::Item`) and maps over a right argument that holds several, with its left argument whole for each. A function that takes text also maps over each row of a character matrix, as `⍎` does. A function that acts asks the running levels where it acts (`Context::acts`), as output and input do: where it reads or writes files, the network, the clock or the terminal, or makes something new on each call, as `•index` and `•rand` do. Any other system function asks nothing, and a batched run calls it once for each cell. The parser resolves a known system name to its table entry (`system::Entry`) through `Builtin::find`, which finds a glyph's primitive too, and `primitive::Builtin` holds either. A system name is therefore a `NodeKind::Function` node, compiles to a constant and resolves to the function that one process-wide table holds for each builtin (`Builtin::function`), as a glyph does. An unknown system name stays a `NodeKind::System` node, which is a VALUE error when evaluated. Code identifies a table function by the `Entry` that its `SystemFunction` records, as in `Entry::PREFS`, never by its name. A function that a factory made, such as `•element "div"`, has no entry, although it shares its factory's name. Native records use ordinary function nodes and calls. Empty native item mapping returns its declared prototype without calling the function. `data::Options` is the shared keyed-option decoder; formats don't invent separate option parsers.
|
|
274
278
|
|
|
275
279
|
`optimize.rs` implements `⇄` on `rustymath::optimize`. The left argument holds the options. When the method can use a derivative (none named, L-BFGS, Levenberg–Marquardt, CG or MINRES), one linearisation at the start (`derivative::linearized`) finds whether `∂` differentiates the function, and gives its value there. `Goal` evaluates the function at points in the start's layout for `minimize`. When the search runs L-BFGS, each value comes from a linearisation that `gradient` then transposes at the same point (`linearized_at`), so a value and its gradient run the function once. With `"solve":$t`, `Residual` is the `Mapping` that `solve` takes, or that `solve_affine` takes with `"linear":$t` as well. Where `∂` differentiates the function, `Residual` gives JVPs and vector–Jacobian products from one record at each point, or from the start's record for an affine function. At a later point where `∂` gives an UNSUPPORTED error, `Residual::jvp` declines, and rustymath takes forward differences for that Jacobian and later ones. A square linear system takes no products with the transpose, so it runs GMRES on A, not the normal equations. The function in option `precondition` gives the preconditioner.
|
|
276
280
|
|
|
@@ -284,7 +288,7 @@ CSV columns, JSON arrays and Python lists share `data::imported`: a non-fill fin
|
|
|
284
288
|
|
|
285
289
|
`tests/core.rs` checks public storage/layout/API behaviour, ownership, parser diagnostics and recovery. Use `equiv_in!` for session workflows and `fails_in` for errors; each expected expression runs in a fresh session after its code, under the settings that the code leaves. `tests/core.rs` never changes settings. The settings are one store for the whole process, so the checks that change them live in `tests/settings.rs`, which runs them in turn in one test, each from the defaults. Both binaries share the helpers in `tests/common/mod.rs`. Keep constructors for representation/prototype/buffer checks. Self-contained language cases belong in `tests/reference/core.bpl`. `cli/tests/cli.rs` and Python's pseudo-terminal tests exercise the installed process/editor boundary.
|
|
286
290
|
|
|
287
|
-
`reference.rs` serves Rust tests and worker requests. Every reference case uses a fresh session; documentation examples share a session within each BPL code block, which starts from the default settings. A reference file's leading `•prefs` line sets the settings of all its cases. Each case runs four times, with the `batch` pref off, then on, then on with fusion's small sizes, then on with its code as the body of a dfn that one session calls twice (`twice`), and each run starts from the defaults and the file's header. The fourth run checks that a dfn's second call, which runs the statements its first call recorded, gives the first call's value, storage kind, error and output. Code that can't be a dfn's body skips it.
|
|
291
|
+
`reference.rs` serves Rust tests and worker requests. Every reference case uses a fresh session; documentation examples share a session within each BPL code block, which starts from the default settings. A reference file's leading `•prefs` line sets the settings of all its cases. Each case runs four times, with the `batch` pref off, then on, then on with fusion's small sizes, then on with its code as the body of a dfn that one session calls twice (`twice`), and each run starts from the defaults and the file's header. The fourth run checks that a dfn's second call, which runs the statements its first call recorded, gives the first call's value, storage kind, error and output. Code that can't be a dfn's body skips it. `tests/reference.rs` checks the cases in a child process of itself for each core, and Python's `check_file` in a worker for each core, because a process evaluates one case at a time. `batched` leaves out the run with the `batch` pref off, and `unfused` the run with the small sizes. Each expectation evaluates after its code, under the settings that the code leaves. `.bpl` files define active coverage. Captured foreign expectations have no exactness; local expectations compare exactness and structural values independently. A local expectation also fixes where storage is mixed: the result must be held in mixed storage exactly where the expectation is, at each level of nesting (`kind_difference`), unless the case's header carries `mixed`. Pending gaps belong in inventory JSONL, not passing error tests. See `tests/reference/README.md` for format and review/activation workflow. `python/basedpl/reference.py`, `bpltests.py` and `scripts/reference.py` own import, capture, inventory and worker checks. `lib/*.bpl` are readable dfn examples; modules import other library files with private relative names.
|
|
288
292
|
|
|
289
293
|
`tests/scaling.rs` measures time/allocation growth and result-size allocation budgets. `tests/speed.rs` reads native/browser budgets from `speed.txt` through `timing.rs`; the worker and Python measurement tools reuse it. `tests/units.rs` reads existing incremental object files and checks unit size without building. Python tests cover conversion/cancellation/frontend behaviour, not a second Rust semantic suite. Combine related checks into readable tests.
|
|
290
294
|
|
|
@@ -311,4 +315,6 @@ Quarto renders saved outputs. `bpl-nb --save` updates notebook outputs without c
|
|
|
311
315
|
|
|
312
316
|
Before approval, verify successful tests for the release commit and the relevant installed artifacts. Reserve isolated wheel installs for packaging-sensitive work or release preparation, using a small relevant subset of existing tests. `ship-rs-build --profile dist` builds the distributed configuration; bare `ship-rs-build` uses release for local checks.
|
|
313
317
|
|
|
318
|
+
Run `cargo test --profile checked` once before each release. That profile is the test profile with debug assertions on, which adds `session()`'s turn check and the standard library's checks in unsafe code. Those checks cost 12 to 55% per call, too much for the test profile that the budgets measure. The speed and scaling tests mark themselves ignored under debug assertions, so the run checks correctness only.
|
|
319
|
+
|
|
314
320
|
Confirm the clean tree and `[workspace.package].version`, then run `ship-release` without flags. Python gets that version through `dynamic = ["version"]`. Fastship pushes/tags the current version, leaves publication to the Release workflow and bumps Cargo without rebuilding. There is no changelog step. Never commit, push, tag or publish without approval.
|
|
@@ -18,4 +18,4 @@ Function. Hanzi: `阶/拣` (`階/揀`). Ranks: `0` monadic, `0 0` dyadic
|
|
|
18
18
|
|
|
19
19
|
Extends through Gamma to non-integral and complex arguments. At a pole, a negative integer, the result is an infinity or NaN, as the gamma function gives it.
|
|
20
20
|
|
|
21
|
-
For nonnegative exact integers, factorial is limited to 100000.
|
|
21
|
+
For nonnegative exact integers, factorial is limited to 100000. Exceeding the limit gives `DOMAIN`.
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Function. Keys: `Alt-9`. Hanzi: `析/或`. Ranks: `0` monadic, `0 0` dyadic
|
|
4
4
|
|
|
5
|
-
`∨Y` gives real/imaginary pairs along a new last axis. Y is a numeric array.
|
|
5
|
+
`∨Y` gives real/imaginary pairs along a new last axis. Y is a numeric array. A real item's imaginary part is a zero of the item's own kind: a number of its type, or `$f` for a Boolean.
|
|
6
6
|
|
|
7
7
|
```bpl
|
|
8
8
|
∨3j4 ⍝ 3 4
|
|
9
9
|
∨3j4 1j2 ⍝ [3 4⋄1 2]
|
|
10
10
|
∨1r3 ⍝ 1r3 0ₓ
|
|
11
|
+
∨1=1 ⍝ $t $f
|
|
11
12
|
```
|
|
12
13
|
|
|
13
14
|
`X∨Y` gives GCD; on Booleans, OR. Pervasive.
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
- `"batched items"`: code with no rule for batches read a batch's items one at a time, even where the batched call then succeeded.
|
|
37
37
|
- `"∂ items"`: code with no rule for [`∂`](../glyphs/partial.qmd) read the items of a value that `∂` tracks one at a time.
|
|
38
38
|
- `"accumulation"`: a sum added items one at a time, as the `accumulations` path `"item by item"` does.
|
|
39
|
+
- `"item lanes"`: a fold or scan by a function with no kernel for its arguments, such as a primitive on mixed storage or a dfn on a lane that no batched map took, called the function on each pair of items in turn.
|
|
40
|
+
- `"number parts"`: monadic [`∨`](../glyphs/vee.qmd) or [`∧`](../glyphs/wedge.qmd) took the parts of integers or exact numbers one at a time, where floats and complex numbers take one pass.
|
|
39
41
|
- `"nested labels"`, `"label ids"` and `"label offsets"`: a selection from a nested array labelled or placed its items one at a time, or read positions that had no plan.
|
|
40
42
|
- `declined` has a row for each faster route that code considered and didn't take. A decline explains why a slower route ran. `route` names the route, `reason` says why code didn't take it, and `items` counts the items that its declines concerned, summed for each route and reason. The routes are:
|
|
41
43
|
- `"fusion"`: a call that could have joined a fused group ran on its own, because of `"16-bit floats"`, `"no float array"`, `"an exact argument"`, `"a result at another width"`, `"keys, names or a renderer"` or `"an argument of another kind or width, or with keys"`. A pending array also ran without a group where its call reads `"a comparison computed outside the graph"`.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A system function is a built-in function whose name starts with [`•`](glyphs/bullet.qmd), as in `•ucs` or `•json`. Type `•` with Alt-y. Names are case-insensitive. Many system functions take their options as a left argument. A function named for a format reads that format, and its [inverse](scripts.ipynb#superscripts) writes it: `•json` reads JSON, and `•json⁻¹` writes it. Each name below links to its page. `]help •name` shows the same page. Built-in values have names that start with `$`, as [Constants and `$` names](system/constants.qmd) describes.
|
|
4
4
|
|
|
5
|
-
A function that takes one string, such as `•json` or `•element`, applies to each string of an array of strings. A function that takes one number, such as `•uuid`, applies to each number of an array. The results form an array of the argument's shape. An empty argument gives an empty array of the function's usual result, such as an empty list of strings from `•src`. A left argument applies whole to each item. `•c`, `•date`, `•path` and `•metadata` have their own rules for arrays, which their pages give.
|
|
5
|
+
A function that takes one string, such as `•json` or `•element`, applies to each string of an array of strings, and to each row of a character matrix, as [`⍎`](glyphs/hydrant.qmd) does. A function that takes one number, such as `•uuid`, applies to each number of an array. The results form an array of the argument's shape. Results from a character matrix's rows combine as [Rank](glyphs/jot-diaeresis.qmd)'s results do. An empty argument gives an empty array of the function's usual result, such as an empty list of strings from `•src`. A left argument applies whole to each item. `•c`, `•date`, `•path` and `•metadata` have their own rules for arrays, which their pages give.
|
|
6
6
|
|
|
7
7
|
In Python, the workspace object `bpl` has each system function under its name without `•`. Options become keyword arguments, as in `bpl.json('{"a": null}', fill=0)`. `.undo` gives the inverse, as in `bpl.json.undo(Y)` for `•json⁻¹`. [Python](python.ipynb) covers the calling conventions.
|
|
8
8
|
|